DestinHistoire

 

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编辑  收藏  举报

导航