考虑只有两个集合
A
A
和的情况。
(1)把不属于
A
A
也不属于的边加上去。
(2)求集合
A
A
的答案时,就在(1)的基础上把属于但不属于
A
A
的边加上去并判定。
(3)求集合的答案时,就在(1)的基础上把属于
A
A
但不属于的边挤上去并判定。
(2)(3)都是在(1)的基础上加边的,所以要写一个可持久化支持撤回上一次操作的并查集来实现(记录下每次father改变的位置,就可以支持撤回)。
回到原问题。由于题目允许离线,所以使用CDQ分治。先把所有集合都没有的边加上。
假设现在递归到询问区间
[l,r]
[
l
,
r
]
,并且不属于询问区间
[l,r]
[
l
,
r
]
的集合的边已经加上。分为两个子区间
[l,mid]
[
l
,
m
i
d
]
和
[mid+1,r]
[
m
i
d
+
1
,
r
]
。
(1)处理
[l,mid]
[
l
,
m
i
d
]
:把属于
[mid+1,r]
[
m
i
d
+
1
,
r
]
但不属于
[l,mid]
[
l
,
m
i
d
]
的边加上,并递归到
[l,mid]
[
l
,
m
i
d
]
。这一步的加边操作,在操作完之后需要撤回。
(2)处理
[mid+1,r]
[
m
i
d
+
1
,
r
]
:也一样,把属于
[l,mid]
[
l
,
m
i
d
]
但不属于
[mid+1,r]
[
m
i
d
+
1
,
r
]
的边加上,并递归到
[mid+1,r]
[
m
i
d
+
1
,
r
]
。同样,这一步的加边操作,在操作完之后也需要撤回。
剩下最后一个问题:递归到
[i,i]
[
i
,
i
]
时,如何判断图的连通性。
由于图原本是连通的,所以递归到
[i,i]
[
i
,
i
]
时,只需要对于集合
i
i
的每条边,判断
u
u
和是否都连通即可。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, M = 2e5 + 5, V = 5, Orz = 5e6 + 5;
int n, m, K, fa[N], X[M], Y[M], q[M][V], pyz[Orz], lpf[Orz],
tim[Orz], times, orz; bool ans[M], is[M];
int cx(int x, bool ad) {
if (!ad) {
while (fa[x] != x) x = fa[x]; return x;
}
if (fa[x] != x)
pyz[++orz] = x, lpf[orz] = fa[x],
tim[orz] = times, fa[x] = cx(fa[x], ad);
return fa[x];
}
void zm(int x, int y) {
times++; int ix = cx(x, 1), iy = cx(y, 1);
if (ix != iy) pyz[++orz] = iy, lpf[orz] = fa[iy],
tim[orz] = times, fa[iy] = ix;
}
void backto(int tar) {
while (tim[orz] > tar) fa[pyz[orz]] = lpf[orz], orz--;
times = tar;
}
void solve(int l, int r) {
int i, j; if (l == r) {
bool flag = 1; for (i = 1; i <= q[l][0]; i++)
flag = flag && cx(X[q[l][i]], 0) == cx(Y[q[l][i]], 0);
ans[l] = flag; return;
}
int mid = l + r >> 1;
for (i = l; i <= r; i++) for (j = 1; j <= q[i][0]; j++) is[q[i][j]] = 0;
for (i = l; i <= mid; i++) for (j = 1; j <= q[i][0]; j++)
is[q[i][j]] = 1; int tmp = times; for (i = mid + 1; i <= r; i++)
for (j = 1; j <= q[i][0]; j++) if (!is[q[i][j]])
zm(X[q[i][j]], Y[q[i][j]]); solve(l, mid); backto(tmp);
for (i = l; i <= r; i++) for (j = 1; j <= q[i][0]; j++) is[q[i][j]] = 0;
for (i = mid + 1; i <= r; i++) for (j = 1; j <= q[i][0]; j++)
is[q[i][j]] = 1; tmp = times; for (i = l; i <= r; i++)
for (j = 1; j <= q[i][0]; j++) if (!is[q[i][j]])
zm(X[q[i][j]], Y[q[i][j]]); solve(mid + 1, r); backto(tmp);
}
int main() {
int i, j; n = read(); m = read();
for (i = 1; i <= m; i++) X[i] = read(), Y[i] = read();
K = read(); for (i = 1; i <= K; i++) {
q[i][0] = read(); for (j = 1; j <= q[i][0]; j++)
is[q[i][j] = read()] = 1;
}
for (i = 1; i <= n; i++) fa[i] = i;
for (i = 1; i <= m; i++) if (!is[i]) zm(X[i], Y[i]); solve(1, K);
for (i = 1; i <= K; i++) puts(ans[i] ? "Connected" : "Disconnected");
return 0;
}