BZOJ1015或洛谷1197 [JSOI2008]星球大戰
阿新 • • 發佈:2018-11-06
BZOJ原題連結
洛谷原題連結
發現正著想毫無思路,所以我們可以考慮倒著思考,把摧毀變成建造。
這樣很容易想到用並查集來維護連通塊,問題也變的很簡單了。
建原圖,先遍歷一遍所有邊,若某條邊的兩端點未被摧毀,那麼合併兩個點,再倒著去列舉被摧毀的點,對於一個點遍歷它的邊,若是未摧毀的點,那麼就用並查集將兩個連通塊合併,並記錄答案即可。
#include<cstdio> using namespace std; const int N = 4e5 + 10; struct eg { int x, y; }; eg a[N]; int fi[N], di[N], ne[N], fa[N], an[N], br[N], l; bool v[N]; inline int re() { int x = 0; char c = getchar(); bool p = 0; for (; c < '0' || c > '9'; c = getchar()) p |= c == '-'; for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0'; return p ? -x : x; } inline void add(int x, int y) { di[++l] = y; ne[l] = fi[x]; fi[x] = l; } int fin(int x) { if (!(fa[x] ^ x)) return x; return fa[x] = fin(fa[x]); } int main() { int i, j, n, m, x, y, k, s; n = re(); m = re(); for (i = 1; i <= m; i++) { a[i].x = re(); a[i].y = re(); add(a[i].x, a[i].y); add(a[i].y, a[i].x); } for (i = 1; i <= n; i++) fa[i] = i; k = re(); for (i = 1; i <= k; i++) { br[i] = re(); v[br[i]] = 1; } s = n - k; for (i = 1; i <= m; i++) if (!v[a[i].x] && !v[a[i].y]) { x = fin(a[i].x); y = fin(a[i].y); if (x ^ y) { fa[x] = y; s--; } } an[k] = s; for (i = k; i; i--) { v[x = br[i]] = 0; s++; for (j = fi[x]; j; j = ne[j]) if (!v[y = di[j]]) { x = fin(x); y = fin(y); if (x ^ y) { fa[x] = y; s--; } } an[i - 1] = s; } for (i = 0; i <= k; i++) printf("%d\n", an[i]); return 0; }