[BZOJ3237][Ahoi2013]連通圖(並查集+CDQ分治)
阿新 • • 發佈:2019-01-28
考慮只有兩個集合和的情況。
(1)把不屬於也不屬於的邊加上去。
(2)求集合的答案時,就在(1)的基礎上把屬於但不屬於的邊加上去並判定。
(3)求集合的答案時,就在(1)的基礎上把屬於但不屬於的邊擠上去並判定。
(2)(3)都是在(1)的基礎上加邊的,所以要寫一個可持久化支援撤回上一次操作的並查集來實現(記錄下每次father改變的位置,就可以支援撤回)。
回到原問題。由於題目允許離線,所以使用CDQ分治。先把所有集合都沒有的邊加上。
假設現在遞迴到詢問區間,並且不屬於詢問區間的集合的邊已經加上。分為兩個子區間 和。
(1)處理:把屬於但不屬於的邊加上,並遞迴到。這一步的加邊操作,在操作完之後需要撤回。
(2)處理:也一樣,把屬於但不屬於的邊加上,並遞迴到。同樣,這一步的加邊操作,在操作完之後也需要撤回。
剩下最後一個問題:遞迴到時,如何判斷圖的連通性。
由於圖原本是連通的,所以遞迴到 時,只需要對於集合的每條邊,判斷和是否都連通即可。
程式碼:
#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;
}