BZOJ-1015 [JSOI2008]星球大战starwar(并查集)
题目描述
给一个 \(n\) 个点 \(m\) 条边的无向图,有 \(k\) 次操作,一次操作为删掉一个点和所有与该点连接的边,求每一次操作后图中连通块的个数(\(1\leq n\leq 2m,1\leq m\leq2\times 10^5\))。
分析
建完图后把所有要被删掉的点删掉,然后逆向操作加点,每次操作后用并查集维护当前连通块数量。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=4e5+10;
int fa[N],ans[N],ID[N];
bool vis[N];
int get(int x)
{
if(x==fa[x])
return x;
return fa[x]=get(fa[x]);
}
struct Edge
{
int from;
int to;
int Next;
}edge[M];
int head[N],num_edge;
void add_edge(int from,int to)
{
edge[++num_edge].from=from;
edge[num_edge].to=to;
edge[num_edge].Next=head[from];
head[from]=num_edge;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d %d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
int k;
cin>>k;
for(int i=1;i<=k;i++)
{
scanf("%d",&ID[i]);
vis[ID[i]]=1;
}
int cnt=n-k;
for(int i=2;i<=2*m;i++)
{
int x=edge[i].from,y=edge[i].to;
if(vis[x]==0&&vis[y]==0)
{
int fx=get(x),fy=get(y);
if(fx!=fy)
fa[fx]=fy,cnt--;
}
}
ans[k+1]=cnt;
for(int i=k;i>=1;i--)
{
int x=ID[i];
cnt++;
vis[x]=0;
for(int j=head[x];j;j=edge[j].Next)
{
int y=edge[j].to;
int fx=get(x),fy=get(y);
if(vis[y]==0&&fx!=fy)
fa[fx]=fy,cnt--;
}
ans[i]=cnt;
}
for(int i=1;i<=k+1;i++)
printf("%d\n",ans[i]);
return 0;
}
posted on 2020-12-08 13:37 DestinHistoire 阅读(83) 评论(0) 编辑 收藏 举报