https://ac.nowcoder.com/acm/contest/883/A
题目大意,给出一个无向图,设S(x)表示x的临近点集合,临近点即通过一条边直接相连的点。有两种操作,1是把边集合(读入顺序)从l到r的边状态反转(相连变断开,断开变相连),2是询问两个点的临近集是否相等。
这里首先涉及到的一个难点是如何表示临近集,我们采用异或哈希的方式,首先为所有点分配一个随机权值,这里用到了一个新版的随机函数
1 | mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); |
接下来我们把某个点的所有临近点的权值异或起来,作为它的临近集表示。异或哈希的大概原理是,原始权值在每个bit上01的概率相等,而异或过程对于每个bit产生01的概率也是等价的,所以能保证随机性。这个问题解决后,就能以O(1)时间判定临近集的相等。
接下来考虑如何维护翻转,一个简单的分块考虑是,对边集分块,最开始计算出每个块对每个点的影响(add数组)(O(m^1.5)),然后对于1操作,标记块的翻转,零碎的边直接计算出来;对于2操作,把所有翻转的块中对该点的影响都异或到现有答案上。这样两个操作的复杂度都是O(m^0.5),只可惜这样会超时,由于是多组数据,而我们在每组数据中都要对add数组进行整体清零,这个复杂度是m^0.5*n的,三四组数据估计就不行了。
所以考虑缩减add数组,我们发现,如果一个点的度数比较小,那么在计算它的临近集时可以挨个枚举相连的边,复杂度是和度数成正比的。因而我们考虑,只把度数大于sqrt(m)的点用add数组维护,这样的点总共不会超过2*sqrt(m)个。剩下的小点需要再维护一下零碎边的翻转性(e数组),同时块的翻转标记也会影响到它。
ps分块题的细节真的一不留神就写错……不过思路直接还是挺容易debug的
1 |
|