[BZOJ1015][JSOI2008]星球大战starwar
[BZOJ1015][JSOI2008]星球大战starwar
试题描述
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的
机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直
接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划
地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首
领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每
一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则
这两个星球在同一个连通块中)。
输入
输入文件第一行包含两个整数,N (1 < = N < = 2M) 和M (1 < = M < = 200,000),分别表示星球的
数目和以太隧道的数目。星球用 0 ~ N-1的整数编号。接下来的M行,每行包括两个整数X, Y,其中(0 < = X <>
Y 表示星球x和星球y之间有“以太”隧道,可以直接通讯。接下来的一行为一个整数k,表示将遭受攻击的星球的
数目。接下来的k行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这k个数互不相同,且都在0到n-1的范
围内。
输出
输出文件的第一行是开始时星球的连通块个数。接下来的N行,每行一个整数,表示经过该次打击后现存星球
的连通块个数。
的连通块个数。
输入示例
8 13 0 1 1 6 6 5 5 0 0 6 1 2 2 3 3 4 4 5 7 1 7 2 7 6 3 6 5 1 6 3 5 7
输出示例
1 1 1 2 3 3
数据规模及约定
见“输入”
题解
离线处理,倒过来做变成合并的问题,然后就是裸的并查集了。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 400010 #define maxm 400010 int n, m, head[maxn], next[maxm], to[maxm], que[maxn]; bool vis[maxn]; void AddEdge(int a, int b) { to[++m] = b; next[m] = head[a]; head[a] = m; swap(a, b); to[++m] = b; next[m] = head[a]; head[a] = m; return ; } int fa[maxn]; int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); } int main() { n = read(); int m = read(); for(int i = 1; i <= m; i++) { int a = read() + 1, b = read() + 1; AddEdge(a, b); } int k = read(); for(int i = 1; i <= k; i++) que[i] = read() + 1, vis[que[i]] = 1; for(int i = 1; i <= n; i++) fa[i] = i; int cnt = n - k; for(int i = 1; i <= n; i++) if(!vis[i]) for(int e = head[i]; e; e = next[e]) if(!vis[to[e]]) { int u = findset(i), v = findset(to[e]); if(u != v) fa[v] = u, cnt--; } que[k+1] = cnt; for(int i = k; i; i--) { vis[que[i]] = 0; cnt++; for(int e = head[que[i]]; e; e = next[e]) if(!vis[to[e]]) { int u = findset(que[i]), v = findset(to[e]); if(u != v) fa[v] = u, cnt--; } que[i] = cnt; } for(int i = 1; i <= k + 1; i++) printf("%d\n", que[i]); return 0; }