BZOJ 1015 - 变删点为加点 + 并查集维护
题意:给出一张无向图,每次删去其中一个点,每删一次就输出当前连通块的数量。
首先要明确一点:删去一个点,同时也删去了和这个点有关联的边集。但无论如何,删点并不好搞,所以我们可以考虑倒着来,加点,用并查集维护。具体来说,每次加上一个点x,如果一个点是被第一次删去的(一个点可能被删去多次)(然而数据中并没有这种情况),那么就将连通块数加1;否则不考虑。然后依次添上它的每一条相邻边,并查集维护之即可。
woc… 无向边M要乘2… 又忘了…
// BZOJ 1015
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=200000*2+5, N=M*2;
#define rep(i,a,b) for (int i=a; i<=b; i++)
#define dep(i,a,b) for (int i=a; i>=b; i--)
#define read(x) scanf("%d", &x)
#define fill(a,x) memset(a, x, sizeof(a))
int fa[N], cnt, n, m, ans[N], u, v, k, t, del[N];
bool exi[N];
int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
struct Graph {
int s, from[M], to[M], pre[M], last[N];
void init() { s=0; rep(i,0,n-1) last[i]=0; }
void ine(int a, int b) {
s++;
from[s]=a, to[s]=b, pre[s]=last[a];
last[a]=s;
}
void ine2(int a, int b) {
ine(a, b);
ine(b, a);
}
} G;
#define reg(i,G,u) for (int i=G.last[u]; i; i=G.pre[i])
void add(int x, int t) {
cnt++;
reg(i,G,x) {
int y=G.to[i];
if (!exi[y]) continue;
int fx=find(x), fy=find(y);
if (fx!=fy) cnt--;
fa[fx]=fy;
}
exi[x]=true;
ans[t]=cnt;
}
int main()
{
read(n); read(m);
G.init();
rep(i,0,n-1) fa[i]=i;
rep(i,1,m) read(u), read(v), G.ine2(u, v);
read(k);
fill(exi, true);
rep(i,1,k) read(del[i]), exi[del[i]]=false;
cnt=0;
rep(x,0,n-1) if (exi[x]) add(x, n+1);
ans[k]=cnt;
dep(i,k,1) {
int x=del[i];
add(x, i-1);
}
rep(i,0,k) printf("%d\n", ans[i]);
return 0;
}