netman's blog

By netman, 10 years ago, In Russian

Всем привет!

Не так давно я решил задачу COT2.

В этой задаче нам дано дерево. Каждому дереву соответствует число. Нужно быстро отвечать на запрос x y — кол-во различных чисел на пути от вершины x до вершины y.

Сначала я написал на неё, как мне казалось верное решение, но оно было не верным, но оно зашло.

Вот это решение — http://pastie.org/8667428.

Идея его была такова:

Обойдем дерево dfs'ом и сохраним все вершины в порядке обхода. Обход назовем v[]. Потом чуть переделаем наши запросы:

Вместо (x,y) будет (l,r), где l и r позиции вершин x и y в нашем обходе v[]. Если l>r, то поменяем l и r местами.

Теперь применим корневую эвристику к запросам, то есть отсортируем запросы по паре (l/block,r), где block ~ sqrt(n).

Теперь будем обрабатывать наши запросы в отсортированном порядке.

То есть, если мы сейчас на пути v[old_l]->v[old_r] и нам надо перейти на путь v[l]->v[r], то мы просто двигаемся по дереву от вершины old_l до вершины l и от вершины old_r до вершины r. При этом, каждый раз посещая какую-нибудь вершину, мы проверяем: лежит ли она на пути v[l]->v[r], если лежит, то проверяем ложили ли мы число этой вершины в ответ, если нет, то ложим(ложить будем числа за O(1) применяя обычный массив меток).

Это есть мое первое решение. Оно зашло, хоть оно неверное. Так как для него есть хороший контр-тест, который дает асимптотику O(N*M).

Этот тест будет таким:

    1
   /|\
  / 2 \
 |     |
 |     |
 .     .
 .     .
 .     .
(n/2) (n)

А запросы будут такого вида:

(n/2,n/2+1), (2,n/2+2), (n/2,n/2+3), ..., (2,n)

Это будет худшим случаем для данного решения, так как мы построим такой обход:

v[]={ 1, 3, 4, 5, ..., n/2, 2, n/2+1, n/2+2, ..., n }

И вершины n/2 и 2 будут находиться рядом, но вот расстояние между ними будет O(N), и если мы будем обрабатывать запросы, в которых мы должны будем переходить на такое расстояние, то будет сложность решения O(N*M).

Но чуть позже я придумал верное решение. Оно отличается от первого решения совсем немного.

Вот код этого решения — http://pastie.org/8667490.

Мы должны поменять обход, чтобы расстояние между соседними вершинами в обходе было O(1). То есть dist(v[i],v[i+1])=O(1).

Делать будем его так:

Почти так же, как и обычный обход.

Будем ходить dfs'ом по дереву, когда заходим в вершину, то добавляем её в обход, если она на четной глубине. Когда выходим из вершины, то добавляем её в обход, если она на нечетной глубине.

Из этого видно что расстояние между соседними вершинами будет примерно 3-4, то есть dist(v[i],v[i+1])=O(1).

И мы получаем честное решение за O(M sqrt N).

Можете рассматривать эту мини-статейку, как разбор к этой задаче :)

Но у меня есть вопрос: Можно ли решить эту задачу с более лучшей асимптотикой, например O(M log N) или O(M log^2 N)? Если да, то как её решить с более лучшей асимптотикой?

  • Vote: I like it
  • +16
  • Vote: I do not like it