bzoj1015

本文提供了一道编号为1015的星球大战问题的详细解答,采用倒序维护并查集的方法,将删除节点转换为添加节点的操作,有效地解决了联通块计数的问题。通过具体的C++代码实现,展示了如何处理路径压缩和避免fa[x]陷阱,最终实现了高效求解。
摘要由CSDN通过智能技术生成

1015 星球大战

  倒序维护并查集,将删点转换成加点,每次操作后并查集个数就是联通块个数。

  不知道为什么set在bzoj上跑的贼慢,洛谷和d......j上都能A在bzoj上T了 换成数组就过了

  注意:倒序合并的时候合并的是getf(x)而不是fa[x]

  因为倒序合并的原因有些路径是没有压缩过的,fa[x]就会出问题(自闭了一个小时)

/**************************************************************

    Problem: 1015

    User: syh0313

    Language: C++

    Result: Accepted

    Time:1764 ms

    Memory:11900 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

using namespace std;

const int maxn=400010;

int n,m,topt,st[maxn],nt[maxn],to[maxn],fa[maxn];

int q[maxn],ans[maxn],num,size;

bool f[maxn],v[maxn],s[maxn];

void add(int x,int y)

{to[++topt]=y; nt[topt]=st[x]; st[x]=topt;}

int getf(int x)

{

    if (fa[x]==x) return x;

    fa[x]=getf(fa[x]);

return fa[x];

}

void comb(int x,int y)

{

    if (getf(x)==getf(y)) return;

    fa[getf(y)]=getf(x);

return;

}

void dfs(int x,int father)

{

    f[x]=1;

    if (getf(x)!=father) comb(x,father);

    int p=st[x];

    while (p)

    {

        if (!f[to[p]]) dfs(to[p],father);

        p=nt[p];

    }

}

int main()

{

    memset(v,1,sizeof v);

    scanf("%d%d",&n,&m);

    for (int i=1;i<=m;i++)

    {

        int xx,yy; scanf("%d%d",&xx,&yy);

        add(xx,yy); add(yy,xx);

    }

    for (int i=0;i<n;i++) fa[i]=i;

    scanf("%d",&num);

    for (int i=1;i<=num;i++) scanf("%d",&q[i]),f[q[i]]=1,v[q[i]]=0;

    for (int i=0;i<n;i++) if (!f[i]) dfs(i,i),s[getf(fa[i])]=1,size++;

    ans[num+1]=size;

    for (int i=num;i;i--)

    {

        int x=q[i]; int p=st[x]; v[x]=1;

        while (p)

        {

            if (!v[to[p]]) {p=nt[p]; continue;}

            int ffa=getf(to[p]);

            if (s[ffa]) s[ffa]=0,size--;

            comb(x,ffa);

            p=nt[p];

        }

        s[getf(fa[x])]=1; size++;

        ans[i]=size;

    }

    for (int i=1;i<=num+1;i++) printf("%d\n",ans[i]);

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值