Hdu 2473 Junk-Mail Filter (并查集的删除)

来源:互联网 发布:华北雾霾 知乎 编辑:程序博客网 时间:2024/06/05 05:50

题意:有n个集合,m个操作。对每个操作输入的如果为M,那就合并两个集合,否则将此点分离出去,问一共有多少个集合。

举例:对于

M 0 2
M 1 2
S  2

合并后的集合是{0,1,2},把2删除掉之后, 集合变为 {0,1}

思路:建立一个映射,通过改变将删除点的映射关系,同时改变其父节点到一个新的、虚设的点。这种方法很费空间。

直接将一个点的父节点改成自身是不行的,因为如果这点已经是一个集合的根节点,那它的父节点本来就等于自身。

#include <cstdio>#include <cstring>const int N=1100005;class Disjoint_Set{public:int father[N];     /*father[x]表示x的父节点*/int rank[N];       //以该节点为根节点的点数,同时起到按秩合并的作用int mapping[N];     //节点映射数组int id;void Init (int n){//本题对应元素为0~n-1for (int i=0;i<n;i++)Make_Set (i);        id=n;        //////}void Make_Set (int x){father[x]=x;mapping[x]=x;rank[x]=1;}int Find_Set (int x){if (x != father[x])father[x] = Find_Set(father[x]);//回溯return father[x];}void Union (int x,int y){int a=Find_Set (x);int b=Find_Set (y);if (a == b)return;if (rank[a] >= rank[b]){father[b]=a;rank[a]+=rank[b];}else{father[a]=b;rank[b]+=rank[a];}}void Delete (int x){//改变待删除点的映射数组    father[id]=id;    mapping[x]=id++;    }}ob;bool visit[N];int main (){int n,m,Cas=1;while (scanf("%d%d",&n,&m),n||m){ob.Init(n);char str[4];int a,b;while (m--)        {            scanf("%s%d",str,&a);            if (str[0]=='M')            {                scanf("%d",&b);  //利用映射值                ob.Union(ob.mapping[a],ob.mapping[b]);            }            else                ob.Delete(a);        }        memset(visit,false,sizeof(visit));        int ans=0;        for (int i=0;i<n;i++)        {            a=ob.Find_Set(ob.mapping[i]);            if (visit[a]==false)            {                ans++;                visit[a]=true;            }        }        printf("Case #%d: %d\n",Cas++,ans);    }    return 0;}


0 0