CF938G
老规矩,对于不可做的这种操作题先考虑没有修改操作怎么做
这时问题就变为了给你一个联通的图,让你找一条\(u->v\)的最短路
我们可以先假设\(u->v\)只有一条简单路径,且权值异或和为d
由于原题保证图是联通的,所以我们可以走到图上所有的环,而可以发现我们此时可多得到的贡献就是图上所有最小简单环的异或和
注意,这里强调最小的原因是如果忽略此性质,会很难求,就算求出来也存不下
但我们可以发现如果两个简单环中有一部分重叠路径,我们把他们疑惑在一起时正好构成了另一个简单环的权值异或和
然后这个问题就变成了一个数d和一个集合异或和最小的问题,可以用线性基解决
然后我们考虑到\(u->v\)不一定只有一条简单路径,所以我们暴力枚举就好
其实我们可以发现我们随意从\(u->v\)的路径中选一条就可以解决这个问题,因为我们一定有一个简单环包含当前随机选的简单路径,我们让这个路径异或上简单环就可以得到另一条路径了
然后我们考虑怎么同时维护添加和删除操作
如果只有添加或删除操作,我们的思路通常时正难则反,倒着考虑情况
而对于同时有添加和删除的题目则通常是:
- 按照时间分治
- 维护可持久化数据结构
对于这题,我们就是用了方法1来实现
我们以线段树的下标作为时间节点维护某一条边的存在时间
注意,这里不同于普通的线段树分治的做法维护当前某一条边的出现和终止时间,而是在线段树上每一个节点开一个vector表示当前这个时间段内存在的这些边
这些标记不用下方,而是作为标记永久化挂在那里,不然空间会爆炸
然后我们便利到整颗线段树上挂有询问的叶子节点,这时我们肯定从线段树的根节点开始走过了一些节点,在这条路径上我们把所有的标记做一些处理,使得可以维护出最小环异或和并维护线性基。这里怎么处理之后再讲。
那么我们只需要在每个叶子节点直接记录即可
注意,因为撤回操作的难度远小于删除操作的难度,这里在回溯到父亲的时候就相当与把删除操作变成了撤回操作
因此,现在问题就变成了:
- 每次加入一条边,且保证每次加入的边无重边
- 撤回上一次操作
- 求出这个图上所有最小简单环异或和所构成的集合的子集和给定的x异或的最小值
这个问题我们虽然可以用可持久化并查集+可持久化线性基的方法,但这带log且代码难度大。而且可持久化并查集支持撤销前k此操作,显然比这个问题要求严格
故此我们采用记录日志的方法来撤回
具体的,如果这次是撤回并查集合并操作,我们记录下每次并查集修改的哪个位置和原来的值,直接暴力赋值成原来的数值
而如果这次是撤回线性基加入操作,同样的,我们记录下每次线性基修改的哪个位置和原来的值,暴力赋值
这样撤回的复杂度是O(1)的
而并查集我们因为维护了日志,我们不能使用路径压缩,而是用按秩合并来保证复杂度
而如果当前加入边(x,y)两点在同一集合内,就说明生成了一个简单环,为了得到他的异或和,我们要求出这个并查集上每个点到根的异或和
因此并查集需要维护以下信息:
- siz/dep
- xorsum
整体复杂度\(O(nlogn)\)