动态图连通性笔记
首先离线的话有几种方法:
- 线段树分治
- 动态维护最大生成树:边的权值为他的(下一次)删除时间,加边正常做,询问时问路径最小值是否小于当前时刻.
动态图连通性 Holm-de Lichtenberg-Thorup (HLT)
-
暴力:维护生成森林,若删树边则暴力找另一条边能替代这条树边.
-
思想:给每条边赋一个“不重要度”. 每次未成功重连则提升之.
-
算法:给每条边赋一个等级 ,初始为 0. 维护 意义下的最大生成森林:(不需显式维护),,维护以下性质:
- 是 的生成森林 连通性与 相同 是以 为边权的最大生成森林(树).(显然.)
- 每个连通块大小 (保证边权上界为 ).
删一条树边 后我们寻找重连边 .
性质 1 告诉我们重连边 满足 ,不然原树不是最大的. 故从 开始按 降序找:
: 上 把树分为 (点集),不妨设 点数较小,把 上等级 的边升到 . 【由于 点数较小,性质 2 仍满足(更具体地, 点数 ),性质 1 显然满足.】然后找 (图上)连出的等级 的边进行重连,若能则结束,否则升一级. 【此时这些边两侧都在 ,而 在 ,显然不破坏性质 1.】若仍未成功,尝试 .
-
时间:由性质 2, 时 就没点了,故 . 然后推理:
- 每条边只被上移 次.(显然)
- 总的来看,每条边只 次出现在 .(因为每次失败都会上移.)
- 将一条边设为树边只需 次树操作.(显然)
- 每次树操作可用 ETT, LCT, Top Tree, AAA 等之一完成,是 .
故修改 ,查询( 在 上查)是 .
-
实现细节:对每个 维护从 出发、等级 的非树边列表;在 上需枚举一些点(一个连通块 / 子树)中有哪些点有等级 的非树边,这个可对所有“有对应等级的点”在动态树上打标记,(显然复杂度对的).
代码咕.
代码目前是 LOJ 最优解(2024.10.7):
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5010, M = 1e6 + 10; int read() { int x = 0; char ch = getchar(); for (; !(48 <= ch && ch <= 57); ch = getchar()); for (; 48 <= ch && ch <= 57; ch = getchar()) x = (x << 3) + (x << 1) + ch - 48; return x; } struct ETT { struct Node { int c[2], fa, sz, t, key1[2], key2[2]; } T[M]; ETT() { for (int i = 1; i < N; ++i) T[i] = (Node){{0, 0}, 0, 1, 1, {0, 0}, {0, 0}}; } #define get(u) (T[T[u].fa].c[1] == u) void up(int u) { int x = T[u].c[0], y = T[u].c[1]; T[u].sz = T[u].t + T[x].sz + T[y].sz; for (int t = 0; t <= 1; ++t) T[u].key2[t] = T[u].key1[t] | T[x].key2[t] | T[y].key2[t]; } void mfa(int u, int v, int w) { T[u].fa = v, T[v].c[w] = u, up(v); } void rot(int u) { int v = T[u].fa, w = get(u); if (T[v].fa) mfa(u, T[v].fa, get(v)); else T[u].fa = 0; mfa(T[u].c[!w], v, w), mfa(v, u, !w); } void splay(int u, int p = 0) { for (; T[u].fa != p; rot(u)) if (T[T[u].fa].fa != p) rot(get(u) ^ get(T[u].fa) ? u : T[u].fa); } template<int t> int get_a(int u) { while (!T[u].key1[t]) u = T[u].c[T[T[u].c[1]].key2[t]]; return splay(u), u; } int get_rt(int u) { splay(u); while (T[u].c[0]) u = T[u].c[0]; return splay(u), u; } void makert(int u) { splay(u); if (!T[u].c[0]) return; int v = u; while (T[v].c[0]) v = T[v].c[0]; splay(v, u), mfa(T[u].c[1], v, 0), mfa(v, u, 1), T[u].c[0] = 0, up(u); } void link(int u, int v, int e, int k) { makert(u), makert(v), T[u].fa = T[v].fa = e ^ 1; T[e ^ 1] = (Node){{u, v}, e, 0, 0, {0, k}, {0, k}}; T[e] = (Node){{e ^ 1, 0}, 0, 0, 0, {0, k}, {0, k}}; up(e ^ 1), up(e); } void cut(int e) { makert(e), T[T[e].c[1]].fa = 0, splay(e ^ 1), T[e ^ 1].c[0] = T[e ^ 1].c[1] = T[T[e ^ 1].c[0]].fa = T[T[e ^ 1].c[1]].fa = 0; } template<int t> void set(int u, int k) { splay(u), T[u].key1[t] = k, up(u); } int sz(int u) { return splay(u), T[u].sz; } int connected(int u, int v) { return (u == v) ? 1 : (splay(u), splay(v), T[u].fa); } } F[15]; int tot, nex[M], pre[M], st[M], vet[M], rk[M]; struct cmp { bool operator()(int x, int y) const { return (st[x] == st[y]) ? vet[x] < vet[y] : st[x] < st[y]; } }; set<int, cmp> G[N], G0[N]; struct HLT { int node(int u, int rank) { return N * rank + u; } HLT() { tot = N * 15 + 1; for (int rk = 0; rk < 15; ++rk) for (int i = 1; i < N; ++i) nex[node(i, rk)] = node(i, (rk + 1) % 15), pre[node(i, rk)] = node(i, (rk + 14) % 15); } void New(int u, int v) { st[++tot] = u, vet[tot] = v, rk[tot] = 0; } void connect(int u, int v) { nex[u] = v, pre[v] = u; } void add(int u, int v, int eg, int rk) { int nu = node(u, rk), nv = node(v, rk); connect(eg, nex[nu]), connect(nu, eg); if (nex[eg] < N * 15) F[rk].set<0>(u, 1); connect(eg ^ 1, nex[nv]), connect(nv, eg ^ 1); if (nex[eg ^ 1] < N * 15) F[rk].set<0>(v, 1); } void del(int u, int v, int eg, int rk) { connect(pre[eg], nex[eg]); if (pre[eg] < N * 15 && nex[eg] < N * 15) F[rk].set<0>(u, 0); connect(pre[eg ^ 1], nex[eg ^ 1]); if (pre[eg ^ 1] < N * 15 && nex[eg ^ 1] < N * 15) F[rk].set<0>(v, 0); } void link(int u, int v) { New(u, v), New(v, u); int eg = tot ^ 1; G0[u].insert(eg), G0[v].insert(eg ^ 1); if (!F[0].connected(u, v)) F[0].link(u, v, eg, 1), G[u].insert(eg), G[v].insert(eg ^ 1); else add(u, v, eg, 0); } void upgrade(int x, int rank) { F[rank].set<1>(x, 0), F[rank].set<1>(x ^ 1, 0), ++rk[x], ++rk[x ^ 1], F[rank + 1].link(st[x], vet[x], x, 1); } int replace(int u, int v, int rank) { for (int i = nex[node(u, rank)]; i > N * 15; i = nex[node(u, rank)]) { int t = vet[i]; del(u, t, i, rank); if (F[rank].connected(t, v)) return i; else ++rk[i], ++rk[i ^ 1], add(u, t, i, rank + 1); } return 0; } void cut(int u, int v) { New(u, v); int eg = tot--; eg = *G0[u].find(eg), G0[u].erase(eg), G0[v].erase(eg ^ 1); if (G[u].find(eg) != G[u].end()) { G[u].erase(eg), G[v].erase(eg ^ 1); int rank = rk[eg]; for (int i = 0; i <= rank; ++i) F[i].cut(eg); for (; rank >= 0; --rank) { if (F[rank].sz(u) > F[rank].sz(v)) swap(u, v); int x = u, res = 0; while (F[rank].splay(x), F[rank].T[x].key2[1]) x = F[rank].get_a<1>(x), upgrade(x, rank); x = u; while (F[rank].splay(x), F[rank].T[x].key2[0] && !res) x = F[rank].get_a<0>(x), res = replace(x, v, rank); if (res) { G[x].insert(res), G[vet[res]].insert(res ^ 1); for (int i = 0; i <= rank; ++i) F[i].link(x, vet[res], res, i == rank); return; } } } else del(u, v, eg, rk[eg]); } int query(int u, int v) { return F[0].get_rt(u) == F[0].get_rt(v); } } hlt; struct Operations { int op, u, v; } Ops[M]; vector<int> g[N]; int qu[N], qv[N], tag[N]; int bfs(int u, int v) { int lu = 0, lv = 0, ru = 0, rv = 0, tp, i; qu[ru++] = u, qv[rv++] = v, tag[u] = 1, tag[v] = 2; if (u == v) return 1; while (1) { if (lu == ru) break; tp = qu[lu++]; for (i = 0; i < g[tp].size(); ++i) { if (tag[g[tp][i]] == 2) return 1; if (!tag[g[tp][i]]) qu[ru++] = g[tp][i], tag[g[tp][i]] = 1; } if (lv == rv) break; tp = qv[lv++]; for (i = 0; i < g[tp].size(); ++i) { if (tag[g[tp][i]] == 1) return 1; if (!tag[g[tp][i]]) qv[rv++] = g[tp][i], tag[g[tp][i]] = 2; } } return 0; } int main() { int n = read(), m = read(), ans = 0, res, op, u, v, sumq = 0; for (int i = 1; i <= m; ++i) Ops[i].op = read(), Ops[i].u = read(), Ops[i].v = read(), sumq += Ops[i].op == 2; if (sumq <= 5000) { for (int i = 1; i <= m; ++i) { op = Ops[i].op, u = Ops[i].u ^ ans, v = Ops[i].v ^ ans; if (op == 0) g[u].insert(lower_bound(g[u].begin(), g[u].end(), v), v), g[v].insert(lower_bound(g[v].begin(), g[v].end(), u), u); if (op == 1) g[u].erase(lower_bound(g[u].begin(), g[u].end(), v)), g[v].erase(lower_bound(g[v].begin(), g[v].end(), u)); if (op == 2) memset(tag, 0, sizeof(tag)), res = bfs(u, v), puts(res ? "Y" : "N"), ans = res ? u : v; } } else { for (int i = 1; i <= m; ++i) { op = Ops[i].op, u = Ops[i].u ^ ans, v = Ops[i].v ^ ans; if (op == 0) hlt.link(u, v); if (op == 1) hlt.cut(u, v); if (op == 2) res = hlt.query(u, v), puts(res ? "Y" : "N"), ans = res ? u : v; } } return 0; }
本文作者:Laijinyi
本文链接:https://www.cnblogs.com/laijinyi/p/18302813/HLT
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步