jerry3128 和 zghtyarecrenj 大佬都讲得很好,但是漏了一些细节。这里简单补充一点。
Holm-de Lichtenberg-Thorup 算法的原始论文:https://u.cs.biu.ac.il/~rodittl/p723-holm.pdf
算法的简化描述如下。
给边分层,每条边一开始都在第 0 层,然后之后可以移到上层,定义
我们维护这几个性质:
对任何非树边
, 和 都在 中连通。 中每个连通分量的节点数最多为 ,这样最大的层号 就是 。 对任何树边
,对任何 , 和 在 中都不连通。
- 查询
和 是否连通
直接查询
- 插入边
令
- 删除边
先看
如果不在
- Reconnect(
, ),在第 层尝试重新连接 和 所在的连通分量
假设
如果
我们假设性质 2 在第
由于最大层数为
从上面的描述中我们可以看到,对一条边时只有以下几种操作:
查询某点在
里的哪棵树中。 用这条边将两棵树连起来,或者将这条边删掉,得到两棵树。
将这条边加入到
中,或者从 中删掉。 将这条边加入到
中,或者从 中删掉。 遍历以
中指定子树里的节点为端点的 和 里的所有边。
其中,查询在哪棵树中,以及在森林里增删树边可以通过用 LCT 或者 ETT
来维护
遍历与指定子树相连的
LCT 的节点只有指向 preferred child 的指针,没有指向 non-preferred children 的指针,但是维护子树信息需要从 non-preferred children 所在的子树的信息。通常这是通过 Top Tree 来实现的,即用 splay 来合并 non-preferred children 所在的子树的信息,然后通过根将合并后的信息传给节点(参考:https://www.cnblogs.com/Khada-Jhin/p/9743397.html)。但是这样代码量太大了。
注意到,我们其实不需要将所有 non-preferred children
所在的子树的信息合并起来,我们只需要从有 tag
的子树中随便选一个,就可以把它的 tag 作为当前节点的 tag
。所以我们只需要在每个节点用一个哈希表来维护所有有 tag 的 non-preferred
children 的编号,更新节点
if (有边连着自己) {
x.tag = x
} else if (x.left_child.tag != NIL) {
x.tag = x.left_child.tag
} else if (x.right_child != NIL) {
x.tag = x.right_child.tag
} else if (x.tagged_non_preferred_children is not empty) {
c = x.tagged_non_preferred_children里的任意一个元素
x.tag = c.tag
} else {
x.tag = NIL
}
向
动态图完全连通性截至2017年的最新成果汇总:https://www.cs.cmu.edu/~anupamg/advalgos17/handouts/italiano-talk-extract.pdf
其中这篇文章做到了
Thorup M. Near-optimal fully-dynamic graph connectivity[C]//Proceedings of the thirty-second annual ACM symposium on Theory of computing. 2000: 343-350.
题目链接:https://www.luogu.com.cn/problem/P5247
完整代码:
#include <iostream>
#include <unordered_set>
#include <stack>
#include <unordered_map>
#include <cstring>
using namespace std;
#define MAXN 5011
#define MAX_LEVEL 15
struct LCT {
int c[MAXN][2], fa[MAXN], sta[MAXN];
bool r[MAXN];
// subtree_size2 is the sum of the sizes of non-preferred children's subtrees
int subtree_size[MAXN], subtree_size2[MAXN];
struct Tag {
// Only stores edges of this level.
<int> edges;
unordered_setint tag;
<int> tagged_non_preferred_children;
unordered_set} tag_tree[MAXN], tag_non_tree[MAXN];
void update_tag(Tag tags[], int x) {
if (!tags[x].edges.empty()) {
[x].tag = x;
tags} else if (tags[ls(x)].tag) {
[x].tag = tags[ls(x)].tag;
tags} else if (tags[rs(x)].tag) {
[x].tag = tags[rs(x)].tag;
tags} else if (!tags[x].tagged_non_preferred_children.empty()) {
[x].tag = tags[*tags[x].tagged_non_preferred_children.begin()].tag;
tags} else {
[x].tag = 0;
tags}
}
void new_non_preferred_child(int x) {
if (fa[x] == 0)
return;
[fa[x]] += subtree_size[x];
subtree_size2if (tag_tree[x].tag)
[fa[x]].tagged_non_preferred_children.insert(x);
tag_treeif (tag_non_tree[x].tag)
[fa[x]].tagged_non_preferred_children.insert(x);
tag_non_tree}
void delete_non_preferred_child(int x) {
if (fa[x] == 0)
return;
[fa[x]] -= subtree_size[x];
subtree_size2if (tag_tree[x].tag)
[fa[x]].tagged_non_preferred_children.erase(x);
tag_treeif (tag_non_tree[x].tag)
[fa[x]].tagged_non_preferred_children.erase(x);
tag_non_tree}
inline int& ls(int rt) {
return c[rt][0];
}
inline int& rs(int rt) {
return c[rt][1];
}
inline bool not_splay_rt(int x) {
return ls(fa[x]) == x || rs(fa[x]) == x;
}
inline int side(int x) {
return x == rs(fa[x]);
}
void Init(int n) {
// Initially every node is a tree by itself.
// memset all to 0.
for (int i = 1; i <= n; ++i) {
[i] = 1;
subtree_size}
}
inline void pushr(int x) {
(ls(x), rs(x));
swap[x] ^= 1;
r}
inline void pushdown(int x) {
if (r[x]) {
if (ls(x))
(ls(x));
pushrif (rs(x))
(rs(x));
pushr[x] = false;
r}
}
inline void __pushup(int x) {
(tag_tree, x);
update_tag(tag_non_tree, x);
update_tag[x] = subtree_size[ls(x)] + subtree_size[rs(x)] + 1 + subtree_size2[x];
subtree_size}
// At first x is not in its tagged_non_preferred_children
inline void __pushup_splay_rt(int x) {
(x);
__pushup(x);
new_non_preferred_child// No need to update tag[fa[x]], because if it was in this subtree, then it is still in this subtree.
}
// tag[x] is not updated.
void __rotate_up(int x) {
int y = fa[x], z = fa[y], side_x = side(x), w = c[x][side_x ^ 1];
[x] = z;
faif (not_splay_rt(y))
[z][side(y)] = x;
cif (w)
[w] = y;
fa[y][side_x] = w;
c[y] = x;
fa[x][side_x ^ 1] = y;
c(y);
__pushup}
// tag[x] is not updated.
// The original splay root is removed from its father's tagged_non_preferred_children.
void __splay(int x) {
int y = x, top = 0;
while(1) {
[++top] = y;
staif (!not_splay_rt(y))
break;
= fa[y];
y }
int to = fa[y];
(y);
delete_non_preferred_childwhile (top)
(sta[top--]);
pushdownwhile (fa[x] != to) {
int y = fa[x];
if (fa[y] != to)
(side(x) == side(y) ? y : x);
__rotate_up(x);
__rotate_up}
}
void splay(int x) {
(x);
__splay(x);
__pushup_splay_rt}
void access(int x) {
int ori_x = x;
for (int w = 0; x; w = x, x = fa[x]) {
(x);
__splay(w);
delete_non_preferred_child(rs(x));
new_non_preferred_child(x) = w;
rs(x);
__pushup_splay_rt}
(ori_x);
__splay(ori_x);
__pushup}
int find_root(int x) {
(x);
accessfor (; ls(x); x = ls(x))
(x);
pushdown(x);
__splay(x);
__pushupreturn x;
}
inline void make_root(int x) {
(x);
access(x);
pushr}
void __link(int x, int y) {
// If simply fa[x] = y, the complexity might be wrong.
(y);
access(x);
pushdown[y] = x;
fa(x) = y;
ls(x); // Might be unnecessary
__pushup}
inline void link_new(int x, int y) {
(x);
make_root(x, y);
__link}
inline void link(int x, int y) {
(x);
make_rootif (find_root(y) == x)
return;
(x, y);
__link}
inline void split(int x, int y) {
(x);
make_root(y);
access}
void cut_existing(int x, int y) {
(x, y);
split[x] = ls(y) = 0;
fa(y); // Might be unnecessary
__pushup}
void cut(int x, int y) {
(x, y);
splitif (ls(y) != x || rs(x) != 0)
return; // No such edge (x, y)
[x] = ls(y) = 0;
fa(y); // Might be unnecessary
__pushup}
std::unordered_set<int> take_out_edges(Tag type[], int x) {
(x);
accessauto tmp = std::unordered_set<int>();
(tmp, type[x].edges);
swap(type, x);
update_tagreturn std::move(tmp);
}
void add_directed_edge(Tag type[], int x, int y) {
if (type[x].edges.empty()) {
(x);
access[x].edges.insert(y);
type(type, x);
update_tag} else {
[x].edges.insert(y);
type}
}
void delete_directed_edge(Tag type[], int x, int y) {
if (type[x].edges.size() == 1) {
(x);
access[x].edges.erase(y);
type(type, x);
update_tag} else {
[x].edges.erase(y);
type}
}
void new_tree_edge(int x, int y) {
(x, y);
link_new(tag_tree, x, y);
add_directed_edge(tag_tree, y, x);
add_directed_edge}
};
struct DynamicConnectivity {
struct LCT F[MAX_LEVEL];
<int, unordered_map<int, int> > level;
unordered_mapvoid Init(int n) {
for (int i = 0; (1 << i) <= n; ++i)
[i].Init(n);
F}
// Assume no duplicate edge
void link_new(int x, int y) {
[x][y] = 0;
level[y][x] = 0;
levelif (F[0].find_root(x) == F[0].find_root(y)) {
[0].add_directed_edge(F[0].tag_non_tree, y, x);
F[0].add_directed_edge(F[0].tag_non_tree, x, y);
F} else {
[0].new_tree_edge(x, y);
F}
}
bool reconnect(int x, int y, int l) {
[l].access(x);
F[l].access(y);
Fif (F[l].subtree_size[x] > F[l].subtree_size[y])
(x, y);
swapwhile (1) {
[l].access(x);
Fint u = F[l].tag_tree[x].tag;
if (u == 0)
break;
auto tmp = F[l].take_out_edges(F[l].tag_tree, u);
for (int v : tmp) {
[l].delete_directed_edge(F[l].tag_tree, v, u);
F[l+1].new_tree_edge(u, v);
F++level[u][v];
++level[v][u];
}
}
= F[l].find_root(y);
y while (1) {
[l].access(x);
Fint u = F[l].tag_non_tree[x].tag;
if (u == 0)
break;
auto tmp = F[l].take_out_edges(F[l].tag_non_tree, u);
do {
auto it = tmp.begin();
int v = *it;
.erase(it);
tmp[l].delete_directed_edge(F[l].tag_non_tree, v, u);
Fif (F[l].find_root(v) == y) {
if (!tmp.empty()) {
[l].access(u);
F(tmp, F[l].tag_non_tree[u].edges);
swap[l].update_tag(F[l].tag_non_tree, u);
F}
for (int i = 0; i < l; ++i)
[i].link_new(u, v);
F[l].new_tree_edge(u, v);
Freturn true;
} else {
[l+1].add_directed_edge(F[l+1].tag_non_tree, u, v);
F[l+1].add_directed_edge(F[l+1].tag_non_tree, v, u);
F++level[u][v];
++level[v][u];
}
} while (!tmp.empty());
};
return false;
}
void cut_existing(int x, int y) {
auto it1 = level[x].find(y);
int l = it1->second;
[x].erase(it1);
level[y].erase(x);
level
auto& s = F[l].tag_non_tree[x].edges;
if (s.find(y) != s.end()) {
[l].delete_directed_edge(F[l].tag_non_tree, x, y);
F[l].delete_directed_edge(F[l].tag_non_tree, y, x);
Freturn;
}
[l].delete_directed_edge(F[l].tag_tree, x, y);
F[l].delete_directed_edge(F[l].tag_tree, y, x);
Ffor (int i = 0; i <= l; ++i)
[i].cut_existing(x, y);
Fwhile (1) {
if (reconnect(x, y, l))
break;
if (l == 0)
break;
--l;
}
}
bool is_connected(int x, int y) {
return F[0].find_root(x) == F[0].find_root(y);
}
};
int main() {
int n, m;
static DynamicConnectivity dc;
("%d%d", &n, &m);
scanf.Init(n);
dcint last = 0;
while (m--) {
int op, x, y;
("%d%d%d", &op, &x, &y);
scanf^= last;
x ^= last;
y switch (op) {
case 0:
.link_new(x, y);
dcbreak;
case 1:
.cut_existing(x, y);
dcbreak;
case 2:
if (dc.is_connected(x, y)) {
("Y");
puts= x;
last } else {
("N");
puts= y;
last }
break;
}
}
return 0;
}