1. 程式人生 > >[BZOJ3237][Ahoi2013]連通圖(並查集+CDQ分治)

[BZOJ3237][Ahoi2013]連通圖(並查集+CDQ分治)

考慮只有兩個集合AB的情況。
(1)把不屬於A也不屬於B的邊加上去。
(2)求集合A的答案時,就在(1)的基礎上把屬於B但不屬於A的邊加上去並判定。
(3)求集合B的答案時,就在(1)的基礎上把屬於A但不屬於B的邊擠上去並判定。
(2)(3)都是在(1)的基礎上加邊的,所以要寫一個可持久化支援撤回上一次操作的並查集來實現(記錄下每次father改變的位置,就可以支援撤回)。
回到原問題。由於題目允許離線,所以使用CDQ分治。先把所有集合都沒有的邊加上。
假設現在遞迴到詢問區間[l,r],並且不屬於詢問區間[l,r]的集合的邊已經加上。分為兩個子區間

[l,mid][mid+1,r]
(1)處理[l,mid]:把屬於[mid+1,r]但不屬於[l,mid]的邊加上,並遞迴到[l,mid]。這一步的加邊操作,在操作完之後需要撤回。
(2)處理[mid+1,r]:也一樣,把屬於[l,mid]但不屬於[mid+1,r]的邊加上,並遞迴到[mid+1,r]。同樣,這一步的加邊操作,在操作完之後也需要撤回。
剩下最後一個問題:遞迴到[i,i]時,如何判斷圖的連通性。
由於圖原本是連通的,所以遞迴到
[i,i]
時,只需要對於集合i的每條邊(u,v),判斷uv是否都連通即可。
程式碼:

#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; }