可以省去换根之类的操作
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) #define pushup(x) (sze[x] = sze[tr[x][0]] + sze[tr[x][1]] + 1) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } inline void write(int x, char ch) { if (x < 0) putchar('-'), x = -x; if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 2e5 + 10; int n, m, fa[N], tr[N][2], sze[N]; void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; pushup(y), pushup(x); } void splay(int x) { while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, pushup(x); } int main() { n = read(); for (int i = 1; i <= n; i++) { int k = read(); sze[i] = 1; if (i + k <= n) fa[i] = i + k; } for (m = read(); m; m--) { int opt = read(), st = read() + 1; if (opt == 1) access(st), splay(st), write(sze[st], ' '); else { int k = read(); access(st), splay(st), tr[st][0] = fa[tr[st][0]] = 0; // cut if (st + k <= n) fa[st] = st + k; // link } } return 0; }
图上询问
题意:给定$n$个点$m$条边的无向图,$q$次询问,每次询问编号在$[l_i,r_i]$的边构成多少连通块,$n,m,q leq 2 imes 10^5$
解法:
对于$r in [1,n]$,求$[1,r]$的最大生成森林(以边的编号为边权)
连通块数=点数-最大生成森林的边数
主席树维护每个前缀$[1,r]$中,每种编号的边是否出现过,即可做到在线解决
$mathcal O((n + m) log (n+m)+q log m)$
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } inline void write(int x, char ch) { if (x < 0) putchar('-'), x = -x; if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 2e5 + 10, M = 2e5 + 10; int n, m, q, f[N + M], fa[N + M], tr[N + M][2], val[N + M]; bool rev[N + M]; int find(int u) { return f[u] == u ? u : f[u] = find(f[u]); } void reverse(int x) { swap(tr[x][0], tr[x][1]), rev[x] ^= 1; } void pushdown(int x) { if (rev[x]) { if (tr[x][0]) reverse(tr[x][0]); if (tr[x][1]) reverse(tr[x][1]); rev[x] = 0; } } void pushall(int x) { if (!isroot(x)) pushall(fa[x]); pushdown(x); } void updata(int x) { val[x] = (x <= n ? 0x3f3f3f3f : x); if (tr[x][0] && val[x] > val[tr[x][0]]) val[x] = val[tr[x][0]]; if (tr[x][1] && val[x] > val[tr[x][1]]) val[x] = val[tr[x][1]]; } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; updata(y), updata(x); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, updata(x); } int findrt(int u) { access(u), splay(u); while (tr[u][0]) pushdown(u), u = tr[u][0]; return splay(u), u; } void makert(int u) { access(u), splay(u), reverse(u); } void link(int u, int v) { makert(u), splay(v), fa[u] = v; } void split(int u, int v) { makert(u), access(v), splay(v); } int rt[M], ls[M * 40], rs[M * 40], sum[M * 40], nodeid; int modify(int pre, int l, int r, int pos, int delta) { int now = ++nodeid; sum[now] = sum[pre], ls[now] = ls[pre], rs[now] = rs[pre]; if (l == r) sum[now] += delta; else { int mid = (l + r) >> 1; if (pos <= mid) ls[now] = modify(ls[pre], l, mid, pos, delta); else rs[now] = modify(rs[pre], mid + 1, r, pos, delta); sum[now] = sum[ls[now]] + sum[rs[now]]; } return now; } int query(int u, int v, int l, int r, int Left, int Right) { if (Left <= l && r <= Right) return sum[u] - sum[v]; int mid = (l + r) >> 1, res = 0; if (Left <= mid) res += query(ls[u], ls[v], l, mid, Left, Right); if (mid < Right) res += query(rs[u], rs[v], mid + 1, r, Left, Right); return res; } int main() { freopen("graph.in", "r", stdin); freopen("graph.out", "w", stdout); n = read(), m = read(), q = read(); for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) { rt[i] = rt[i - 1]; int u = read(), v = read(); if (u == v) continue; if (find(u) != find(v)) f[find(u)] = find(v), link(u, i + n), link(i + n, v); else { split(u, v); int now = val[v]; splay(now), fa[tr[now][0]] = fa[tr[now][1]] = 0; link(u, i + n), link(i + n, v); rt[i] = modify(rt[i], 1, m, now - n, -1); } rt[i] = modify(rt[i], 1, m, i, 1); } for (int i = 1; i <= q; i++) { int l = read(), r = read(); int tot = query(rt[r], rt[l - 1], 1, m, l, m); write(n - tot, ' '); } return 0; }
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } const int N = 5e4 + 5, M = 1e5 + 5; int n, m, ans, val[N + M], tr[N + M][2], fa[N + M], Max[N + M], id[N + M], f[N]; bool rev[N + M]; struct edge { int u, v, a, b; void readdata() { u = read(), v = read(), a = read(), b = read(); } bool operator < (const edge &g) const { return a < g.a; } } e[M]; int find(int u) { return f[u] == u ? u : f[u] = find(f[u]); } void updata(int u) { Max[u] = val[u], id[u] = u; if (tr[u][0] && Max[u] < Max[tr[u][0]]) Max[u] = Max[tr[u][0]], id[u] = id[tr[u][0]]; if (tr[u][1] && Max[u] < Max[tr[u][1]]) Max[u] = Max[tr[u][1]], id[u] = id[tr[u][1]]; } void reverse(int u) { swap(tr[u][0], tr[u][1]), rev[u] ^= 1; } void pushdown(int u) { if (rev[u]) { if (tr[u][0]) reverse(tr[u][0]); if (tr[u][1]) reverse(tr[u][1]); rev[u] = 0; } } void pushall(int u) { if (!isroot(u)) pushall(fa[u]); pushdown(u); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; updata(y), updata(x); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x], z = fa[y]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, updata(x); } int findrt(int u) { access(u), splay(u); while (tr[u][0]) pushdown(u), u = tr[u][0]; return splay(u), u; } void makert(int u) { access(u), splay(u), reverse(u); } void link(int u, int v) { makert(v), splay(u), fa[v] = u; } void cut(int u, int v) { makert(v), splay(v), fa[u] = tr[v][1] = 0, updata(v); } void split(int u, int v) { makert(u), access(v), splay(v); } int main() { n = read(), m = read(), ans = 0x3f3f3f3f; for (int i = 1; i <= m; i++) e[i].readdata(); sort(e + 1, e + m + 1); for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) { val[i + n] = e[i].b; int u = e[i].u, v = e[i].v; if (u == v) continue; if (find(u) != find(v)) link(u, i + n), link(i + n, v), f[find(u)] = find(v); else { split(u, v); int now = id[v], b = Max[v]; if (b <= e[i].b) continue; splay(now), fa[tr[now][0]] = fa[tr[now][1]] = 0; link(u, i + n), link(i + n, v); } if (find(1) == find(n)) split(1, n), ans = min(ans, Max[n] + e[i].a); } cout << (ans == 0x3f3f3f3f ? -1 : ans) << endl; return 0; }
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } inline void write(int x, char ch) { if (x < 0) putchar('-'), x = -x; if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 5e4 + 10, M = 2e5 + 10; struct edge { int u, v, w; void readdata() { u = read(), v = read(), w = read(); } bool operator < (const edge &g) const { return w < g.w; } } e[M]; int n, m, ans = 0x3f3f3f3f, cnt, f[N], fa[N + M], tr[N + M][2], val[N + M], Min[N + M], id[N + M]; bool vis[M], rev[N + M]; int l; int find(int u) { return f[u] == u ? u : f[u] = find(f[u]); } void updata(int u) { Min[u] = val[u], id[u] = u; if (tr[u][0] && Min[u] > Min[tr[u][0]]) Min[u] = Min[tr[u][0]], id[u] = id[tr[u][0]]; if (tr[u][1] && Min[u] > Min[tr[u][1]]) Min[u] = Min[tr[u][1]], id[u] = id[tr[u][1]]; } void reverse(int u) { swap(tr[u][0], tr[u][1]), rev[u] ^= 1; } void pushdown(int u) { if (rev[u]) { if (tr[u][0]) reverse(tr[u][0]); if (tr[u][1]) reverse(tr[u][1]); rev[u] = 0; } } void pushall(int u) { if (!isroot(u)) pushall(fa[u]); pushdown(u); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; updata(y), updata(x); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, updata(x); } void makert(int u) { access(u), splay(u), reverse(u); } int findrt(int u) { access(u), splay(u); while (tr[u][0]) pushdown(u), u = tr[u][0]; return u; } void link(int u, int v) { makert(u), splay(u), fa[u] = v; } void cut(int u, int v) { makert(u), splay(u), fa[v] = tr[u][1] = 0, updata(u); } void split(int u, int v) { makert(u), access(v), splay(v); } int main() { n = read(), m = read(); for (int i = 1; i <= m; i++) e[i].readdata(); sort(e + 1, e + m + 1); for (int i = 1; i <= n; i++) f[i] = i, val[i] = 0x3f3f3f3f; cnt = n; for (int i = 1; i <= m; i++) { int u = e[i].u, v = e[i].v; val[i + n] = e[i].w; if (u == v) continue; if (find(u) != find(v)) f[find(u)] = find(v), cnt--, link(u, i + n), link(i + n, v), vis[i] = true; else { split(u, v); int now = id[v]; splay(now), fa[tr[now][0]] = fa[tr[now][1]] = 0, vis[now - n] = false; link(u, i + n), link(i + n, v), vis[i] = true; } while (!vis[l]) l++; if (cnt == 1) ans = min(ans, e[i].w - e[l].w); } write(ans, ' '); return 0; }
逆向考虑
优先级最低的一定是最后删的
在这之前是先删优先级次低的到优先级最低的链上的点
再之前是删优先级第三低的到优先级次低和优先级最低的链上的点
我们把优先级最低的作为根
按优先级从高到低,依次将它们到根的路径染上一种比之前都深的颜色(后面的覆盖前面的,染色过程相当于$access$操作)
发现把某点优先级设为最低也相当于$makeroot$操作
查询时则是维护$u$所在的实链中深度大于它的点数加上比它浅的实链的总点数
第一部分$splay$上直接维护,第二部分加个树状数组即可
有个东西我tmd改到自闭,就树状数组的上界是$n+m$,可是对于多组询问修改,我习惯这样写(((((((
这是什么傻逼。。。
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) #define pushup(x) (sze[x] = sze[tr[x][0]] + sze[tr[x][1]] + 1) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } inline void write(int x, char ch) { if (x < 0) putchar('-'), x = -x; if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 5e5 + 10; int n, m, fa[N], tr[N][2], sze[N], id[N], tot, sum[N << 1]; bool rev[N]; int head[N], to[N << 1], nxt[N << 1], cnt; void add_edge(int u, int v) { to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt; } void dfs(int u, int las) { fa[u] = las; for (int i = head[u]; i; i = nxt[i]) { int v = to[i]; if (v == las) continue; dfs(v, u); } } void add(int k, int delta) { for ( ; k <= n + m; k += (k & -k)) sum[k] += delta; } int ask(int k) { int res = 0; for ( ; k; k ^= (k & -k)) res += sum[k]; return res; } void reverse(int x) { swap(tr[x][0], tr[x][1]), rev[x] ^= 1; } void pushdown(int x) { if (rev[x]) { if (tr[x][0]) reverse(tr[x][0]); if (tr[x][1]) reverse(tr[x][1]); rev[x] = false; } if (tr[x][0]) id[tr[x][0]] = id[x]; if (tr[x][1]) id[tr[x][1]] = id[x]; } void pushall(int x) { if (!isroot(x)) pushall(fa[x]); pushdown(x); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; pushup(y), pushup(x); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } pushup(x); } void access(int x) { int d = 0, u = x, y = 0; for (y = 0; x; y = x, x = fa[x]) { splay(x); int s = 1; if (tr[x][0]) s += sze[tr[x][0]]; if (id[x]) add(id[x], -s); pushdown(x); d += s, tr[x][1] = y, pushup(x); } splay(y), add(id[y] = ++tot, d); // 先splay才能保证整条链改 } void makert(int x) { access(x), splay(x), reverse(x); } int main() { n = read(), m = read(); for (int i = 1; i < n; i++) { int u = read(), v = read(); add_edge(u, v), add_edge(v, u); } dfs(n, 0); for (int i = 1; i <= n; i++) access(i); for (int i = 1; i <= m; i++) { char opt = getchar(); while (opt != 'M' && opt != 'Q') opt = getchar(); if (opt == 'M') makert(read()); else { int u = read(); splay(u); write(ask(id[u]) - sze[tr[u][0]], ' '); } } return 0; }
删边不好搞,把操作倒序进行,则删边改为加边
每次加边就是把$LCT$维护的那棵树上对应两点之间的路径给$split$出来,打个全赋为$0$的标记
就不写了吧,思路错了别找我哇2333
类似$DDP$,将虚儿子$size$和记做$s[u]$,总的$size$和记做$sze[u]$,然后转移
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) #define pushup(x) (sze[x] = sze[tr[x][0]] + sze[tr[x][1]] + s[x] + 1) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } inline void write(long long x, char ch) { if (x < 0) putchar('-'), x = -x; if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 1e5 + 10; int n, m, fa[N], tr[N][2], s[N], sze[N]; bool rev[N]; void reverse(int x) { swap(tr[x][0], tr[x][1]), rev[x] ^= 1; } void pushdown(int x) { if (rev[x]) { if (tr[x][0]) reverse(tr[x][0]); if (tr[x][1]) reverse(tr[x][1]); rev[x] = false; } } void pushall(int x) { if (!isroot(x)) pushall(fa[x]); pushdown(x); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; pushup(y), pushup(x); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), s[x] += sze[tr[x][1]], s[x] -= sze[tr[x][1] = y]/*, pushup(x)*/; } void makert(int x) { access(x), splay(x), reverse(x); } void split(int u, int v) { makert(u), access(v), splay(v); } void link(int u, int v) { makert(u), access(v), splay(v), s[fa[u] = v] += sze[u], pushup(v); } // 注意这里要access,才无需再向上更新 int main() { n = read(), m = read(); for (int i = 1; i <= n; i++) sze[i] = 1; for (int i = 1; i <= m; i++) { char opt = getchar(); while (opt != 'A' && opt != 'Q') opt = getchar(); int u = read(), v = read(); if (opt == 'A') link(u, v); else { split(u, v); write((s[u] + 1ll) * (s[v] + 1ll), ' '); } } return 0; }
每次修改的只有可能是该点往上的一段连续区间
$val[u]$为$u$儿子中为$1$的节点个数
记录$splay$所维护实链上深度最大的$val$非$1$节点和$val$非$2$节点
对链上进行区间改和单点改
区间改所改的子树只有可能全为$1$或全为$2$,$pushdown$时只需要$swap$深度最大的非$1$节点和非$2$节点
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } const int N = 5e5 + 10; int n, fa[3 * N], tr[N][2], deg[N], val[3 * N], id1[N], id2[N], tag[N]; queue<int> q; void pushup(int x) { id1[x] = id2[x] = -1; int ls = tr[x][0], rs = tr[x][1]; if (rs) id1[x] = id1[rs], id2[x] = id2[rs]; if (id1[x] == -1 && val[x] != 1) id1[x] = x; if (id2[x] == -1 && val[x] != 2) id2[x] = x; if (ls && id1[x] == -1) id1[x] = id1[ls]; if (ls && id2[x] == -1) id2[x] = id2[ls]; } void change(int u, int delta) { if (u) tag[u] += delta, val[u] += delta, swap(id1[u], id2[u]); } void pushdown(int u) { if (tag[u]) { if (tr[u][0]) change(tr[u][0], tag[u]); if (tr[u][1]) change(tr[u][1], tag[u]); tag[u] = 0; } } void pushall(int u) { if (!isroot(u)) pushall(fa[u]); pushdown(u); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; pushup(y); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } pushup(x); } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, pushup(x); } int main() { n = read(); for (int i = 1; i <= n; i++) { int a = read(), b = read(), c = read(); fa[a] = fa[b] = fa[c] = i, deg[i] = 3; } for (int i = n + 1; i <= 3 * n + 1; i++) val[i] = read(), q.push(i); while (q.size()) { int u = q.front(), v = fa[u]; q.pop(); val[v] += (u > n ? val[u] : (int)(val[u] >= 2)), deg[v]--; if (deg[v] == 0) q.push(v); } int ans = (val[1] >= 2); for (int T = read(); T; T--) { int u = read(), op = (val[u] ^= 1); access(u = fa[u]), splay(u); if (op) { if (id1[u] == -1) change(u, 1), ans ^= 1; else splay(u = id1[u]), change(tr[u][1], 1), val[u]++, pushup(u); } else { if (id2[u] == -1) change(u, -1), ans ^= 1; else splay(u = id2[u]), change(tr[u][1], -1), val[u]--, pushup(u); } puts(ans ? "1" : "0"); } return 0; }
涂色操作相当于$LCT$的$access$操作
定义$val[x]$为$x$到根的路径的权值
路径$x ightarrow y$的权值为$val[x]+val[y]-2 imes val[lca(x,y)]+1$
维护$val$则考虑$access$时,$u$点的父亲边由虚变实$u$子树$-1$,由实变虚子树$+1$
注意$LCT$上的父子关系不代表原树上的父子关系
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) #define lson u << 1, l, mid #define rson u << 1 | 1, mid + 1, r using namespace std; inline int read() { int out = 0; bool flag = false; register char cc = getchar(); while (cc < '0' || cc > '9') { if (cc == '-') flag = true; cc = getchar(); } while (cc >= '0' && cc <= '9') { out = (out << 3) + (out << 1) + (cc ^ 48); cc = getchar(); } return flag ? -out : out; } inline void write(int x, char ch) { if (x < 0) putchar('-'), x = -x; if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 1e5 + 10; int n, m, fa[N], tr[N][2], dfn[N], id[N], times, f[N][17], sze[N], dep[N]; int head[N], to[N << 1], nxt[N << 1], cnt; void add_edge(int u, int v) { to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt; } void dfs(int u, int las) { f[u][0] = fa[u] = las, dfn[u] = ++times, id[times] = u, sze[u] = 1, dep[u] = dep[las] + 1; for (int i = 1; i <= 16; i++) f[u][i] = f[f[u][i - 1]][i - 1]; for (int i = head[u]; i; i = nxt[i]) { int v = to[i]; if (v == las) continue; dfs(v, u), sze[u] += sze[v]; } } int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); for (int i = 16; i >= 0; i--) if (dep[f[x][i]] >= dep[y]) x = f[x][i]; if (x == y) return x; for (int i = 16; i >= 0; i--) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0]; } int Max[N << 2], inc[N << 2]; void build(int u, int l, int r) { if (l == r) Max[u] = dep[id[l]]; // dep的下标是点的编号 else { int mid = (l + r) >> 1; build(lson), build(rson); Max[u] = max(Max[u << 1], Max[u << 1 | 1]); } } void pushdown(int u) { Max[u << 1] += inc[u], Max[u << 1 | 1] += inc[u]; inc[u << 1] += inc[u], inc[u << 1 | 1] += inc[u]; inc[u] = 0; } void modify(int u, int l, int r, int Left, int Right, int delta) { if (Left <= l && r <= Right) Max[u] += delta, inc[u] += delta; else { pushdown(u); int mid = (l + r) >> 1; if (Left <= mid) modify(lson, Left, Right, delta); if (mid < Right) modify(rson, Left, Right, delta); Max[u] = max(Max[u << 1], Max[u << 1 | 1]); } } int query(int u, int l, int r, int Left, int Right) { if (Left <= l && r <= Right) return Max[u]; pushdown(u); int mid = (l + r) >> 1, ans = 0; if (Left <= mid) ans = max(ans, query(lson, Left, Right)); if (mid < Right) ans = max(ans, query(rson, Left, Right)); return ans; } int query(int u, int l, int r, int pos) { if (l == r) return Max[u]; pushdown(u); int mid = (l + r) >> 1; if (pos <= mid) return query(lson, pos); else return query(rson, pos); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; } void splay(int x) { while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } int findrt(int x) { while (tr[x][0]) x = tr[x][0]; return x; } void add(int u, int delta) { if (u) { u = findrt(u); // 注意LCT上的父子关系不代表原树上的父子关系 modify(1, 1, n, dfn[u], dfn[u] + sze[u] - 1, delta); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), add(tr[x][1], 1), add(tr[x][1] = y, -1); } int ask(int u) { return query(1, 1, n, dfn[u], dfn[u] + sze[u] - 1); } int main() { n = read(), m = read(); for (int i = 1; i < n; i++) { int u = read(), v = read(); add_edge(u, v), add_edge(v, u); } dfs(1, 0), build(1, 1, n); for (int i = 1; i <= m; i++) { int op = read(); if (op == 1) access(read()); else if (op == 2) { int x = read(), y = read(), Lca = lca(x, y); x = query(1, 1, n, dfn[x]), y = query(1, 1, n, dfn[y]), Lca = query(1, 1, n, dfn[Lca]); // 注意单点查也要考虑到dfn[x]才是线段树上的位置 write(x + y - 2 * Lca + 1, ' '); } else write(ask(read()), ' '); } return 0; }
调了一年终于过了,各种傻逼错误,我好菜哇
可以并行计算$k$位
设$f_0$为一开始全为$0$,最后会变成什么
$f_1$为一开始全为$1$,最后会变成什么
因为这k位间互不影响,所以每组询问最后再$Theta(k)$计算答案
但注意维护时需要翻转链
所以需要同时维护每条实链上从浅到深的信息和从深到浅的信息
$reverse$时记得要交换
$Theta(n+q (k+log n))$
#include <bits/stdc++.h> #define check(x) (tr[fa[x]][1] == x) #define isroot(x) (tr[fa[x]][0] != x && tr[fa[x]][1] != x) using namespace std; typedef unsigned long long ull; ull read() { ull out = 0; register char cc = getchar(); while (cc < '0' || cc > '9') cc = getchar(); while (cc >= '0' && cc <= '9') out = (out << 3) + (out << 1) + (cc ^ 48), cc = getchar(); return out; } void write(ull x, char ch) { if (x == 0) putchar('0'); else { int num = 0; char cc[22]; while (x) cc[++num] = x % 10 + 48, x /= 10; while (num) putchar(cc[num--]); } putchar(ch); } const int N = 1e5 + 10; int head[N], to[N << 1], nxt[N << 1], cnt; void add_edge(int u, int v) { to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt; } int n, m, k, opt[N], fa[N], tr[N][2]; ull a, b, l0[N], l1[N], r0[N], r1[N], val[N]; bool rev[N]; void pushup(int x) { if (opt[x] == 1) l0[x] = r0[x] = 0, l1[x] = r1[x] = val[x]; else if (opt[x] == 2) l0[x] = r0[x] = val[x], l1[x] = r1[x] = (~0ull); else if (opt[x] == 3) l0[x] = r0[x] = val[x], l1[x] = r1[x] = (~val[x]); int ls = tr[x][0], rs = tr[x][1]; if (ls) { ull p = l0[x], q = l1[x]; // 因为分两步合并,所以合并时要先把原来值储存下来 l0[x] = (l0[ls] & q) | ((~l0[ls]) & p); l1[x] = (l1[ls] & q) | ((~l1[ls]) & p); p = r0[x], q = r1[x]; r0[x] = (p & r1[ls]) | ((~p) & r0[ls]); r1[x] = (q & r1[ls]) | ((~q) & r0[ls]); } if (rs) { ull p = l0[x], q = l1[x]; l0[x] = (p & l1[rs]) | ((~p) & l0[rs]); l1[x] = (q & l1[rs]) | ((~q) & l0[rs]); p = r0[x], q = r1[x]; r0[x] = (r0[rs] & q) | ((~r0[rs]) & p); r1[x] = (r1[rs] & q) | ((~r1[rs]) & p); } } void reverse(int x) { swap(tr[x][0], tr[x][1]), swap(l0[x], r0[x]), swap(l1[x], r1[x]), rev[x] ^= 1; } void pushdown(int x) { if (rev[x]) { if (tr[x][0]) reverse(tr[x][0]); if (tr[x][1]) reverse(tr[x][1]); rev[x] = false; } } void pushall(int x) { if (!isroot(x)) pushall(fa[x]); pushdown(x); } void rotate(int x) { int y = fa[x], z = fa[y], opt1 = check(x), opt2 = check(y); if (!isroot(y)) tr[z][opt2] = x; fa[x] = z; tr[y][opt1] = tr[x][opt1 ^ 1], fa[tr[x][opt1 ^ 1]] = y; tr[x][opt1 ^ 1] = y, fa[y] = x; pushup(y), pushup(x); } void splay(int x) { pushall(x); while (!isroot(x)) { int y = fa[x]; if (!isroot(y)) rotate(check(x) == check(y) ? y : x); rotate(x); } } void access(int x) { for (int y = 0; x; y = x, x = fa[x]) splay(x), tr[x][1] = y, pushup(x); } void makert(int x) { access(x), splay(x), reverse(x); } void split(int x, int y) { makert(x), access(y), splay(y); } void dfs(int u, int las) { fa[u] = las; for (int i = head[u]; i; i = nxt[i]) { int v = to[i]; if (v == las) continue; dfs(v, u); } pushup(u); } int main() { n = read(), m = read(), k = read(); for (int i = 0; i < k; i++) b |= (1ull << i); for (int i = 1; i <= n; i++) opt[i] = read(), val[i] = read(); for (int i = 1; i < n; i++) { int u = read(), v = read(); add_edge(u, v), add_edge(v, u); } dfs(1, 0); for ( ; m; m--) { int op = read(), x = read(), y = read(); ull z = read(); //注意z类型为ull if (op == 1) { split(x, y); ull ans = 0; for (int i = k - 1; i >= 0; i--) { if (l0[y] & (1ull << i)) ans |= (1ull << i); else if ((1ull << i) <= z && (l1[y] & (1ull << i))) ans |= (1ull << i), z -= (1ull << i); } write(max(ans, l0[y]), ' '); } else opt[x] = y, val[x] = z, splay(x); } return 0; }