HDU2473 Junk-Mail Filter(并查集删点)

本题是并查集删点的经典例题,并查集删点操作的核心就是虚点,利用res数组映射新点,这样就不会对原来的内容造成干扰。

例如 1,2,3,4 ,这些点再普通操作下变成一个集合,根节点是4。

那么如果我们要删除4,我们考虑将4映射到5上,值得一提的是,不是直接把p[4]=5

而是我们在刚开始就设置一个res数组,表示res[4]=5,之后,p[5]=5,这样做的目的是,

不对当前点进行修改,而是更改了当前点的映射,再对映射后的点进行修改。如此操作我们就会发现

123的根节点依旧是4,而4映射到5这个点上,根节点是5,这样就分离成两个集合了,而我们在这之后永远不会对4本身进行操作,如果想操作4,都会操作到5上

这也就是为什么删除这个点不会影响原先绑定在它上面的其他点。

那么我们在计算集合数量的时候,也不能像以前一样计算,而是利用哈希数组计算是否重复出现

因为 假如123的根节点是4,我们分离4后,又合并4,这时候,5又可能连回到4上,但是我们都是通过res数组进行计算(因为他代表了真实的信息),因此就会产生没有根节点是本身的情况。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string> 
#include<set>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1e5+1e6+10;
int p[N];
int res[N];
int idx;
int find(int x){
    if(p[x]!=x){
        p[x]=find(p[x]);
    }
    return p[x];
}
void change(int x){
    res[x]=idx;
    p[idx]=idx;
    idx++;
}
int h[N];
int main(){
   int i;
   int t=1;
   int n,m;
   while(cin>>n>>m){
        if(n==0&&m==0)
        break;
        for(i=0;i<n;i++){
        res[i]=i; 
           p[i]=i;
           idx=n;
    } 
        for(i=1;i<=m;i++){
            string s;
            cin>>s;
            if(s=="M"){
                int a,b;
            cin>>a>>b;
            int pa=find(res[a]),pb=find(res[b]);
            if(pa!=pb){
            p[pa]=pb;
            }        
        }
        else{
            int a;
            cin>>a;
            change(a);
        }
    }
    int ans=0;
    memset(h,0,sizeof h);
    for(i=0;i<n;i++){
        int x=find(res[i]);
        if(!h[x])
        h[x]=1,ans++;
    }
    printf("Case #%d: %d\n", t++, ans);
   }
   
}
View Code

 

posted @ 2020-02-26 17:11  朝暮不思  阅读(163)  评论(0编辑  收藏  举报