欢迎光临 ~|

Laijinyi

园龄:1年7个月粉丝:2关注:2

动态图连通性笔记

首先离线的话有几种方法:

  • 线段树分治
  • 动态维护最大生成树:边的权值为他的(下一次)删除时间,加边正常做,询问时问路径最小值是否小于当前时刻.

动态图连通性 Holm-de Lichtenberg-Thorup (HLT)

  • 暴力:维护生成森林,若删树边则暴力找另一条边能替代这条树边.

  • 思想:给每条边赋一个“不重要度”. 每次未成功重连则提升之.

  • 算法:给每条边赋一个等级 (e),初始为 0. 维护 意义下的最大生成森林:Gi=(V=V(G),E={eE(G)(e)i})(不需显式维护),Fi=GiF,维护以下性质:

    1. FiGi 的生成森林 Fi 连通性与 Gi 相同 Fi 是以 (e) 为边权的最大生成森林(树).(显然.)
    2. Gi 每个连通块大小 n2i(保证边权上界为 log2n).

    删一条树边 e=(v,w) 后我们寻找重连边 reconnect(e,(e)).

    性质 1 告诉我们重连边 e 满足 (e)(e),不然原树不是最大的. 故从 (e) 开始按 降序找:

    reconnect(e=(v,w),k)Fke 把树分为 Tv,Tw(点集),不妨设 Tv 点数较小,把 Tv 上等级 =k 的边升到 k+1. 【由于 Tv 点数较小,性质 2 仍满足(更具体地,Tv 点数 2),性质 1 显然满足.】然后找 Tv (图上)连出的等级 k 的边进行重连,若能则结束,否则升一级. 【此时这些边两侧都在 Tv,而 TvFk+1,显然不破坏性质 1.】若仍未成功,尝试 reconnect(e,k1).

  • 时间:由性质 2,i>log2nGi 就没点了,故 (e)log2n. 然后推理:

    1. 每条边只被上移 logn 次.(显然)
    2. 总的来看,每条边只 logn 次出现在 reconnect.(因为每次失败都会上移.)
    3. 将一条边设为树边只需 O(logn) 次树操作.(显然)
    4. 每次树操作可用 ETT, LCT, Top Tree, AAA 等之一完成,是 O(logn).

    故修改 O(log2n),查询(F0 上查)是 O(logn).

  • 实现细节:对每个 x,l 维护从 x 出发、等级 l 的非树边列表;在 Fk 上需枚举一些点(一个连通块 / 子树)中有哪些点有等级 k 的非树边,这个可对所有“有对应等级的点”在动态树上打标记,(显然复杂度对的).

代码咕.

代码目前是 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 中国大陆许可协议进行许可。

posted @   Laijinyi  阅读(85)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起