zcmu 1435 盟国(并查集增删裸题)

【题目】

1435: 盟国

【题意】

给定一组操作进行结盟和退盟,最后输出最终有多少个联盟。

【思路】

并查集的增删。

增。想必大家都会了不必多言。删,之前没接触过,现在来学习总结一下。

对于删除操作,在所有节点都直接连接在根节点上的并查集中,理论上只要把要删除的节点的上级重新指向自己就可以了。但是在实际情况中,我们的并查集形成的树的形态都是不可预估形态的,如果直接将一个节点指向自己可能会将他的下级和他一块删除,这就和题意相违背了= =。所以需要进行删除操作的就要处理一下:

定义一个数组f[]存放虚根,f[i]存放的是元素 i 的编号,这个编号在程序运行过程中是逐渐变化的。
当删除了 元素 i 以后,将元素 i 的编号更改为当前最大的编号+1(pos),再把pre[i]赋值为新的f[i]。
这样可以使得元素 i 原先的同组元素不受影响,且元素 i 被分配到一个只包含元素 i 的新组。

【代码】

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <list>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <iostream>
#define mem(a) memset(a,0,sizeof(a))
#define go(i,a,b) for(int i=a;i<=b;i++)
#define og(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int maxn=1000005;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef unsigned long long ull;
int pre[maxn],f[maxn],vis[maxn];
int n,pos;
void init(){go(i,0,n-1) pre[i]=i,f[i]=i;}
int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);}
void join(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y) pre[y]=x;
}
void del(int n)
{
    f[n]=pos;
    pre[pos]=pos;
    pos++;
}
main()
{
    int m,a,b,t=0; char c;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        init(); pos=n;
        while(m--)
        {
            scanf(" %c%d",&c,&a);
            if(c=='M') scanf("%d",&b),join(f[a],f[b]);
            else del(a);
        }
        int ans=0; mem(vis);
        go(i,0,n-1)
        {
            vis[find(f[i])]++;
            if(vis[find(f[i])]==1) ans++;
        }
        printf("Case #%d: %d\n",++t,ans);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值