倍增并查集

如果你既会倍增,又会并查集,那你一定会倍增并查集吧!

link

数据范围明示 \(O(n^2)\) 无法通过。

众所周知,倍增是 \(O(\log n)\) 的,并查集近似 \(O(n)\) 的,它们结合一下不就能过了吗?

先来重新考虑一下倍增的本质。

倍增

倍增最经典的用法是 ST 表。我们将一个区间拆成两个长为 \(2^k\) 的区间,维护每一个区间的信息,以便于 \(O(1)\) 回答。

这个做法中最重要的一步是把长区间拆成小区间,并且必须确保两个小区间的和能合并得到大区间的答案,即我们维护的东西需要满足可合并律即我们将大区间拆成的小区间可以有交,但是其并集必须是原来的大区间

比如求 lca 时,我们正是根据两点往上跳后的 lca 与原来相同这一性质进行合并。

回到这道题。我们发现对于每个点直接维护一个并查集是 \(O(n^2)\) 的,问题的根源在于每次操作需要对多个点更新并查集,并且更新后的祖先是相同的。这种东西显然可以优化。我们令 \(f_{i,j}\) 代表从 \(i\) 开始长度为 \(2^j\) 的区间在并查集上的祖先是谁。

考虑一下怎么维护。对于两个要合并的区间 \([l_1,r_1]\)\([l_2,r_2]\),设 \(len=r_1-l_1+1\)\(k\) 为最大的 \(2^k\le l\),我们选择合并并查集 \(f_{l_1,k}\text{ and }f_{l_2,k}\),合并 \(f_{r_1-2^k+1,k}\text{ and }f_{r_2-2^k+1}\),这样做的正确性是显然的。

我们明确一下同属于一个并查集的区间的含义,这代表所有的这些区间都是完全一致的,这就为我们提供了一种可能的下放操作。我们考虑怎么把第 \(k\) 层的关系转移到 \(k-1\) 层。显然可以考虑将两个区间拆成四个小区间,其长度正好为 \(2^{k-1}\),我们将小区间对应合并,正确性依然是有保证的。

最后,我们看一下第 \(k=0\) 层的不同并查集数目,统计答案即可。

总结一下,首先对于每个点开 \(\log n\) 个并查集,对于每种操作合并对应的并查集。最后将并查集由第 \(k\) 层转移到第 \(k-1\) 层,统计答案。

code

代码写的出奇的顺利,把细节处理好就可以了。

Another Problem

link

考虑这道题和上一道的联系。显然,除了区间相同的定义不一样和某些点强制不同之外,其它没有区别。

于是想一下怎么变成一样的东西。显然可以将序列复制一遍后倒着接在原序列后面,这样就将大部分问题转换成了刚才的问题。现在考虑如何判断强制不同的点,我们只需判断它们是否位于同一个并查集内即可。

至于输出方案,贪心即可。

但是写了才发现贪心有多难。这里借鉴了 syz 的处理思路,将存在矛盾的集合连边,从头开始对各个位置进行赋值,连边的集合中所有已经赋过值的显然会对当前赋值产生一些限制。具体而言,我们只能贪心的填这些值的 \(\operatorname{mex}\),注意不考虑 \(0\)

需要注意的细节较多,要将序列前半部分和后半部分的对应点放到同一个集合中,但是不放也能过,感觉是数据水了。

code

posted @ 2024-04-18 13:13  BYR_KKK  阅读(299)  评论(0)    收藏  举报