Умова «кожне значення на шляху трапляється парну кількість разів» — це класичне запрошення до XOR-хешування.
Зіставимо кожному значенню випадкове -бітове число (наприклад, через хеш-таблицю з генератором випадкових чисел). Тоді для будь-якої множини ребер XOR їхніх хешів дорівнює нулю з переважною ймовірністю саме тоді, коли кожне значення трапляється парну кількість разів: значення, що входять двічі, взаємно знищуються, а випадкові хеші «випадково» занулитися майже не можуть.
Зауважимо, що ребра лише додаються, і ребро, яке замикає цикл, нічого не змінює (шлях між уже зв'язаними вершинами вже існує). Тому структура завжди залишається лісом, а «простий шлях» — єдиним.
Основна величина. Для кожної вершини зберігатимемо — XOR хешів усіх ребер на шляху від до кореня її компоненти. Тоді XOR уздовж шляху між і однієї компоненти дорівнює
бо спільна частина до кореня взаємно знищується. Отже, шлях гарний тоді й лише тоді, коли \mathrm{hs}[u] = \mathrm{hs}[v].
Запити.
Тип 2 — якщо вершини в різних компонентах, виводимо ; інакше порівнюємо і .
Тип 3 — кількість гарних пар у компоненті дорівнює \sum_{x} \binom{\mathrm{cnt}[x]}{2}, де — скільки вершин компоненти мають значення \mathrm{hs} = x. Підтримуємо цю суму для кожного кореня.
Тип 4 — те саме, але сумарно по всьому лісу; тримаємо один глобальний лічильник.
Додавання ребра. З'єднуючи компоненти, треба перерахувати у всіх вершинах однієї з них: якщо приєднуємо компоненту до кореня компоненти через ребро зі значенням , то всі вершин треба поXORити зі сталою , що визначається і поточними значеннями кінців ребра.
Щоб це було швидко, застосуємо об'єднання меншого до більшого: для кожного кореня тримаємо список його вершин і хеш-таблицю , а переносимо завжди меншу компоненту. Кожна вершина змінює компоненту щонайбільше \mathcal{O}(\log n) разів, тому загальна робота — \mathcal{O}(n \log n) переносів.
Під час переносу кожної вершини акуратно оновлюємо три величини: прибираємо її внесок \binom{\cdot}{2} зі старої компоненти, змінюємо , додаємо внесок у нову компоненту — і паралельно правимо глобальний лічильник.
Загальна складність — \mathcal{O}((n + q)\log n) у середньому. Беріть -бітні хеші: з -бітними ймовірність колізії на такій кількості пар уже помітна.