Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)
题目链接 2016 ACM-ICPC EC-Final Problem G
题意 给定一个无向图。每个点有一种颜色。
现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过$w$的边走到$x$的点的集合中,哪一种颜色的点出现的次数最多。
次数相同时输出编号最小的那个颜色。强制在线。
求哪种颜色可以用线段树合并搞定。
关键是这个强制在线。
当每次询问的时候,我们先要求出最小生成树在哪个时刻恰好把边权值不超过$w$的边都用并查集合并了。
在做最小生成树的时候每合并两个节点,另外开一个新的结点,原来两个点的父亲都指向这个新的结点。
然后倍增预处理,用类似求$LCA$的方法来得到询问的那个时刻。
时间复杂度$O(nlogn)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 2e5 + 10; const int M = N * 20; struct node{ int x, y, z; void scan(){ scanf("%d%d%d", &x, &y, &z); } friend bool operator < (const node &a, const node &b){ return a.z < b.z; } } e[N]; int T, ca = 0; int tot; int n, m, q, ans; int c[N], root[N], v[N], father[N]; int ls[M], rs[M], mx[M], ret[M]; int id, res[N]; int f[N][19]; int getfather(int x){ return father[x] == x ? x : father[x] = getfather(father[x]); } void up(int i){ mx[i] = max(mx[ls[i]], mx[rs[i]]); if (mx[i] == mx[ls[i]]) ret[i] = ret[ls[i]]; else ret[i] = ret[rs[i]]; } int build(int l, int r, int val){ int x = ++tot; ls[x] = rs[x] = mx[x] = ret[x] = 0; if (l == r){ mx[x] = 1; ret[x] = val; return x; } int mid = (l + r) >> 1; if (val <= mid) ls[x] = build(l, mid, val); else rs[x] = build(mid + 1, r, val); up(x); return x; } int Merge(int x, int y, int l, int r){ if (x == 0 || y == 0) return x + y; if (l == r){ mx[x] += mx[y]; return x; } int mid = (l + r) >> 1; ls[x] = Merge(ls[x], ls[y], l, mid); rs[x] = Merge(rs[x], rs[y], mid + 1, r); up(x); return x; } int main(){ scanf("%d", &T); while (T--){ tot = 0; scanf("%d%d", &n, &m); rep(i, 1, n) scanf("%d", c + i); rep(i, 1, n){ father[i] = i; v[i] = 0; f[i][0] = i; root[i] = build(1, n, c[i]); res[i] = ret[root[i]]; } id = n; rep(i, 1, m) e[i].scan(); sort(e + 1, e + m + 1); rep(i, 1, m){ int x = e[i].x, y = e[i].y, z = e[i].z; int fx = getfather(x), fy = getfather(y); if (fx ^ fy){ ++id; f[id][0] = id; father[id] = id; v[id] = z; father[fx] = father[fy] = id; f[fx][0] = f[fy][0] = id; root[id] = Merge(root[fx], root[fy], 1, n); res[id] = ret[root[id]]; } } rep(j, 1, 17){ rep(i, 1, id) f[i][j] = f[f[i][j - 1]][j - 1]; } printf("Case #%d:\n", ++ca); scanf("%d", &q); ans = 0; while (q--){ int x, w; scanf("%d%d", &x, &w); x ^= ans, w ^= ans; dec(i, 17, 0) if (v[f[x][i]] <= w) x = f[x][i]; printf("%d\n", ans = res[x]); } } return 0; }