合并
并查集
可撤销并查集模板:
struct UFS {
stack<ipair> stk;
int f[N], d[N];
void init(int x) { L(i, 1, x) d[i] = 0, f[i] = i; }
int find(int x) { return f[x] == x ? x : find(f[x]); }
int merge(int x, int y) {
x = find(x), y = find(y);
if(x == y) return 0;
if(d[x] > d[y]) swap(x, y);
stk.push(mkp(x, d[x] == d[y])), f[x] = y, d[y] += (d[x] == d[y]);
return 1;
}
void revoke() {
int u = stk.top().first;
d[f[u]] -= stk.top().second, f[u] = u, stk.pop();
}
} ufs;
P6185 [NOI Online #1 提高组]序列
先把 \(t = 2\) 的都用并查集缩点,然后对于 \(t = 1\) 的看看这个是不是二分图,如果是那么判一下两边的需要变化的差等不等于 \(0\), 否则判一下变化的和是不是 \(2\) 的倍数即可。
CF891C Envy
考虑最小生成树使得了边权小的边出现的次数大,也就是一张图的最小生成树的 每种边权出现次数 以及 联通性 一定。因此可以对于每一种边权单独考虑。
那么考虑对于每一条边维护出如果连接了比他 严格 更小的所有边,连的是哪两个联通块。查询的时候对于每一种边权连一下这两个联通块,如果出现环就判无解。
这可以用可撤销并查集维护。
启发式合并
CF1257G Divisor Set
把连向相同节点的点合并就好了
CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
把路径的权值 \(c\) 更改为 \(2^{c}\),记录每一个点 \(x\) 到根的权值异或和 \(f_x\)。一条 \(u\) 到 \(v\) 路径合法相当于是 \(f_u \oplus f_v\) 为 \(0\) 或在二进制下只有一位。
如果保留重子树留下的 \(cnt\) 数组,对于轻子树暴力查表,那么复杂度就是 \(27 n \log n\) 的了。注意一个节点的答案要向子节点取 \(\max\)
线段树合并
CF246E Blood Cousins Return
这题就是用线段树合并维护子树的深度信息。对于每一个位置(名字为 \(s\), 深度为 \(p\)),在 \(dfs\) 过程中让他和 上一次出现 \(s\) 且深度 = \(p\) 的位置 的 \(lca\) 打个深度 \(p\) 减一的标记,然后线段树合并即可。
CF893F Subtree Minimum Query
直接维护一下子树的深度的线段树合并,然后查询也很方便了(
P7323 [WC2021] 括号路径
对于两个用同一种颜色的边连向一个相同的节点,那么他们两个可以合并成一个节点,最终查询每一个集合的大小。
这个可以用 \(map +\) 启发式合并维护,维护用一种颜色连接到这个节点的点是哪个,和这个节点连向哪个节点。
时间复杂度 \(\Theta(m \log ^2 n)\)。
(同时 P6279 [USACO20OPEN]Favorite Colors G 是该题减弱版)