HDU 2473 Junk-Mail Filter (并查集的删除操作)

并查集的删除操作中心思想:

初始化的时候,假使每个节点的root[i]=i(每个点的父节点都是它自己),然后删除某个父节点的时候,该节点可能有多个子结点,就不能保证该节点的其他节点仍然在这个集合里面。


解决方法是:将这些点的父节点指向别的数,删除该点时将该点的父节点指向集合以外的数。(说不清楚,详情见代码)


对于样例:
5 6
M 0 1
M 1 2
M 1 3
S 1
M 1 2
S 3

我们有如下的过程:
初始:
这里写图片描述

M 0 1:
这里写图片描述

M 1 2:
这里写图片描述
M 1 3:
这里写图片描述
S 1:
这里写图片描述
M 1 2:
这里写图片描述
S 3:
这里写图片描述
因为删除操作不会涉及到这些额外附加的节点,因此,我们删除的节点总是没有任何孩子节点,故保证了算法的正确性。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
const int N = (1e+5) +100;
const int maxn = (1e+6) + 3*N ; 
int root[maxn],tot;
int n,m;
void init(int n)
{
    tot = 2*n;
    for(int i=0;i<n;i++)
        root[i]=i+n;//将0~n这些点的父节点往后移n个单位
    for(int i=n;i<=tot+m;i++)
        root[i]=i;//其余可能用到的点的父节点仍然指向自己
    return;
}
int find(int x)//查找函数没有改变
{
    int y=x;
    while(y!=root[y]) y=root[y];
    while(x!=root[x])
    {
        int px=root[x];
        root[x]=y;
        x=px;
    }
    return y;
}
void Union(int x,int y)//合并函数也不做改变
{
    int fx = find(x);
    int fy = find(y);

    if(fx==fy) return;
    root[fx]=fy;
    return;
}
void Delet(int x)//删除该节点时只需要将其指向集合以外的点
{
    root[x] = tot++;
    return;
}
int main()
{
    int cas=0;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0 && m==0) break;
        init(n);
        while(m--)
        {
            char op; 
            int x,y;
            scanf(" %c",&op);
            if(op=='M') scanf("%d%d",&x,&y), Union(x,y);
            else  scanf("%d",&x),Delet(x);
        }
        int ans=0;
        bool vis[maxn];
        memset(vis,true,sizeof(vis));
        for(int i=0;i<n;i++)
            if(vis[find(i)]) ans++,vis[find(i)]=false;
        printf("Case #%d: %d\n",++cas,ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值