[JSOI2008]星球大战(并查集)

[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;
}

这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值