n ≤ 5000 點,m ≤ 200,000 條初始邊,q ≤ 10,000 次操作。D a b 刪除邊,A a b 添加邊,Q 查詢當前連通塊數量。D 操作涉及的邊 → del[u][v] = true。!del[u][v] 的初始邊 → 得到縮點映射 id[u]。ans(並查集根數量)。id[u] != id[v] 加入鄰接表(保留重邊計數 cnt[u][v])。Q:輸出 ans。D a b:u=id[a], v=id[b];cnt[u][v]--;若歸零且 !dfs(u,v) 則 ans++。A a b:u=id[a], v=id[b];若 cnt[u][v]==0 && !dfs(u,v) 則 ans--;cnt[u][v]++。bool dfs(int u, int target) {
if (u == target) return true;
vis[u] = true;
for (int v : adj[u]) {
if (cnt[u][v] > 0 && !vis[v]) {
if (dfs(v, target)) return true;
}
}
return false;
}
由於縮點後圖較小,DFS 代價可接受(數據不強)。
#includeusing namespace std; const int MAXN = 5005; const int MAXM = 530005; struct Edge { int to, next; } e[MAXM]; int head[MAXN], ecnt; int dsu[MAXN]; bool del[MAXN][MAXN]; int cnt[MAXN][MAXN]; bool vis[MAXN]; int ans; int find(int x) { return dsu[x]==x?x:dsu[x]=find(dsu[x]); } void unite(int x, int y) { dsu[find(y)] = find(x); } void addEdge(int u, int v) { e[ecnt] = {v, head[u]}; head[u] = ecnt++; } bool dfs(int u, int t) { if (u == t) return true; vis[u] = true; for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].to; if (cnt[u][v] > 0 && !vis[v]) { if (dfs(v, t)) return true; } } return false; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector > edges(m); for (auto &[u,v] : edges) cin >> u >> v; int q; cin >> q; struct Op { char type; int u, v; }; vector ops(q); for (int i = 0; i < q; ++i) { cin >> ops[i].type; if (ops[i].type != 'Q') { cin >> ops[i].u >> ops[i].v; if (ops[i].type == 'D') del[ops[i].u][ops[i].v] = del[ops[i].v][ops[i].u] = true; } } // 1. 初始化 DSU iota(dsu+1, dsu+n+1, 1); memset(head, -1, sizeof(head)); ecnt = 0; // 2. 合併永不刪除的邊 for (auto [u,v] : edges) { if (!del[u][v]) unite(u, v); } // 3. 縮點映射 for (int i = 1; i <= n; ++i) dsu[i] = find(i); // 4. 統計初始連通塊 ans = 0; for (int i = 1; i <= n; ++i) if (dsu[i] == i) ++ans; // 5. 構建縮點圖(僅跨塊邊) for (auto [u,v] : edges) { int U = dsu[u], V = dsu[v]; if (U != V) { cnt[U][V]++; cnt[V][U]++; if (cnt[U][V] == 1) { // 首條邊加入鄰接表 addEdge(U, V); addEdge(V, U); } } } // 6. 處理操作 for (auto &op : ops) { if (op.type == 'Q') { cout << ans << '\n'; } else { int U = dsu[op.u], V = dsu[op.v]; if (op.type == 'D') { if (--cnt[U][V] == 0) { memset(vis, 0, sizeof(vis)); if (!dfs(U, V)) ++ans; } } else { // 'A' if (cnt[U][V] == 0) { memset(vis, 0, sizeof(vis)); if (!dfs(U, V)) --ans; } ++cnt[U][V]; } } } return 0; }
O(m α(n))。N' ≤ 20000,邊數 M' ≤ 20000。O(N' + M'),共 q × O(N') ≤ 2×10⁸,數據弱可過;強數據需 LCT/ETT 動態樹。O(N'²) 計數陣列(本題 N'≤5000 可用靜態二維陣列)。O(log n) 單次操作。O((n+q)√q)。本題展示了 **「離線縮點 + 縮小圖規模 + 簡單算法應付動態操作」** 的經典套路。
關鍵步驟:標記刪除邊 → 並查集合併永存邊 → 縮點 → 縮點圖上暴力 DFS。
若遇強動態連通性,請裝備 LCT/ETT。