Luogu 3302 [SDOI2013]森林
BZOJ 3123
丑陋的常数,BZOJ 19968ms 卡过。
感觉几个思想都挺经典但是挺套路的。
先考虑不连边的情况怎么做,区间第$k$小,想到主席树,每一个结点维护它到根的前缀和,这样子每一次查询$x$到$y$链上的点就相当于主席树上$sum(x) + sum(y) - sum(lca(x, y)) - sum(fa(lca(x, y)))$,时间复杂度一个$log$。
然后想一想连边怎么做,因为我们要维护一个森林的形态,如果暴力连边的话,相当于把$y$作为根建树连到$x$上,我们有一个优化暴力的办法就是每一次都把$siz$小的那一个点建树连到另一个点。
然后发现这样的复杂度就已经对了,证明方法和树剖类似,相当于每一次合并$siz$都会至少扩大一倍,所以对于一个大小为$n$的集合,至多会合并$logn$次,总复杂度$O(nlogn)$,这东西有一个名字叫做启发式合并。
时间复杂度$O(nlog^{2}n)$。
还有一个算是坑点的东西:
(我也是)
虽然感觉题目说得很清楚了,但是我还是没有看
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 80005; const int Lg = 18; const int M = 4e7 + 5; const int inf = 1 << 30; int testCase, n, m, qn, tot, head[N]; int ans, maxn, ufs[N], siz[N]; int fa[N][Lg], dep[N], a[N], v[N]; bool vis[N]; struct Edge { int to, nxt; } e[N << 1]; inline void add(int from, int to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } struct Innum { int val, id; } in[N]; bool cmp(const Innum &x, const Innum &y) { if(x.val != y.val) return x.val < y.val; else return x.id < y.id; } inline void chkMax(int &x, int y) { if(y > x) x = y; } inline void discrete() { sort(in + 1, in + 1 + n, cmp); in[0].val = -inf; for(int cnt = 0, i = 1; i <= n; i++) { if(in[i].val != in[i - 1].val) chkMax(maxn, ++cnt); a[in[i].id] = cnt; v[cnt] = in[i].val; } } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void init() { for(int i = 1; i <= n; i++) ufs[i] = i, siz[i] = 1; } int find(int x) { return ufs[x] == x ? x : ufs[x] = find(ufs[x]); } namespace PSegT { struct Node { int lc, rc, sum; } s[M]; int root[N], nodeCnt; #define mid ((l + r) >> 1) inline void up(int p) { if(!p) return; s[p].sum = s[s[p].lc].sum + s[s[p].rc].sum; } void ins(int &p, int l, int r, int x, int pre) { s[p = ++nodeCnt] = s[pre], s[p].sum++; if(l == r) return; if(x <= mid) ins(s[p].lc, l, mid, x, s[pre].lc); else ins(s[p].rc, mid + 1, r, x, s[pre].rc); up(p); } int query(int x, int y, int z, int w, int l, int r, int k) { if(l == r) return l; int now = s[s[x].lc].sum + s[s[y].lc].sum - s[s[z].lc].sum - s[s[w].lc].sum; if(k <= now) return query(s[x].lc, s[y].lc, s[z].lc, s[w].lc, l, mid, k); else return query(s[x].rc, s[y].rc, s[z].rc, s[w].rc, mid + 1, r, k - now); } } using namespace PSegT; void dfs1(int x, int fat) { vis[x] = 1, dep[x] = dep[fat] + 1, fa[x][0] = fat; for(int i = 1; i <= 16; i++) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs1(y, x); } } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } inline int getLca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); for(int i = 16; i >= 0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i]; if(x == y) return x; for(int i = 16; i >= 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } void dfs2(int x) { root[x] = 0, ins(root[x], 1, maxn, a[x], root[fa[x][0]]); for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x][0]) continue; dfs2(y); } } inline void solve() { int x, y, k; read(x), read(y), read(k); x ^= ans, y ^= ans, k ^= ans; int z = getLca(x, y), w = fa[z][0]; int res = query(root[x], root[y], root[z], root[w], 1, maxn, k); printf("%d\n", ans = v[res]); } int main() { read(testCase); for(testCase = 1; testCase--; ) { read(n), read(m), read(qn); memset(head, 0, sizeof(head)); tot = maxn = 0; init(); for(int i = 1; i <= n; i++) { read(a[i]); in[i] = (Innum) {a[i], i}; } discrete(); for(int x, y, i = 1; i <= m; i++) { read(x), read(y); add(x, y), add(y, x); int fx = find(x), fy = find(y); if(fx != fy) { ufs[fx] = fy; siz[fy] += siz[fx]; } } memset(vis, 0, sizeof(vis)); memset(root, 0, sizeof(root)); nodeCnt = 0; for(int i = 1; i <= n; i++) { if(vis[i]) continue; dfs1(find(i), 0), dfs2(find(i)); } ans = 0; for(char op[3]; qn--; ) { scanf("%s", op); if(op[0] == 'Q') solve(); else { int x, y; read(x), read(y); x ^= ans, y ^= ans; int fx = find(x), fy = find(y); if(siz[fx] > siz[fy]) swap(fx, fy); add(x, y), add(y, x); dfs1(y, x), dfs2(y); ufs[fy] = fx, siz[fx] += siz[fy]; } } } return 0; }