[JSOI2008]星球大战
题目概述:
给定一张无向图,接下来按一定顺序删除节点及与它相连的边,包括(第一次删除前)每次都要求输出图上的连通块个数。
数据规模:
N<=400000,M<=200000
思路:
这个题经过刚才概述简化后,其实思路比较明显了,因为是按一定顺序删边和点,所以首先是逆序建边点的思想,然后就是每次用一个并查集来记录当前所有点所属的连通块(集合)。别忘了把开始时的情况初始化了,再一个是一个技巧,当我们执行unionn操作时,就判断两个节点是否在同一个连通块内,然后直接让当前图的连通块数-1,当然了,在每次建边点的时候,当前图的连通块的个数+1,所以这其实是一个递推的过程。代码写的比较明了,可以借代码来了解一下。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
int i,j,l,m,n,temp;
int y[400001],nxt[400001],hd[400001];
int b[400001],a[400001],nowans;
int fa[400001],ans[400001];
int r()
{
int aans=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
{
aans*=10;
aans+=ch-'0';
ch=getchar();
}
return aans;
}
void add(int xx,int yy)
{
nxt[++temp]=hd[xx];
y[temp]=yy;
hd[xx]=temp;
}
int findd(int xx)
{
if(fa[xx]==xx) return xx;
fa[xx]=findd(fa[xx]);
return fa[xx];
}
int unionn(int xx,int yy)
{
int fx=findd(xx);
int fy=findd(yy);
if(fx!=fy)
fa[fx]=fy,nowans--;
}
void update(int x)
{
j=a[x];
{
if(!b[j])
{
for(int p=hd[j];p;p=nxt[p])
{
if(!b[y[p]])
{
unionn(j,y[p]);
}
}
}
}
ans[x]=nowans;
}
void update1()
{
for(j=0;j<n;j++)
{
if(!b[j])
{
for(int p=hd[j];p;p=nxt[p])
{
if(!b[y[p]])
{
unionn(j,y[p]);
}
}
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
n=r(),m=r();
int xx,yy;
for(i=0;i<n;i++)
{
fa[i]=i;
}
for(i=1;i<=m;i++)
{
xx=r(),yy=r();
add(xx,yy);
add(yy,xx);
}
l=r();
for(i=1;i<=l;i++)
{
a[i]=r();
b[a[i]]=1;
}
nowans=n-l;
update1();
ans[l+1]=nowans;
for(i=l;i>=1;i--)
{
b[a[i]]=0;
nowans++;
update(i);
}
for(i=1;i<=l+1;i++)
printf("%d\n",ans[i]);
return 0;
}