hdu 2473 Junk-Mail Filter【并查集好题、建立虚拟节点】

来源:互联网 发布:支付宝怎么跟淘宝绑定 编辑:程序博客网 时间:2024/06/06 15:37

Junk-Mail Filter

Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8640    Accepted Submission(s): 2735

Problem Description

Recognizing junk mails is a tough task. The method used here consists of two steps:
1) Extract the common characteristics from the incoming email.
2) Use a filter matching the set of common characteristics extracted to determine whether the email is a spam.

We want to extract the set of common characteristics from the N sample junk emails available at the moment, and thus having a handy data-analyzing tool would be helpful. The tool should support the following kinds of operations:

a) “M X Y”, meaning that we think that the characteristics of spam X and Y are the same. Note that the relationship defined here is transitive, so
relationships (other than the one between X and Y) need to be created if they are not present at the moment.

b) “S X”, meaning that we think spam X had been misidentified. Your tool should remove all relationships that spam X has when this command is received; after that, spam X will become an isolated node in the relationship graph.

Initially no relationships exist between any pair of the junk emails, so the number of distinct characteristics at that time is N.
Please help us keep track of any necessary information to solve our problem.

 

 

Input

There are multiple test cases in the input file.
Each test case starts with two integers, N and M (1 ≤ N ≤ 105 , 1 ≤ M ≤ 106), the number of email samples and the number of operations. M lines follow, each line is one of the two formats described above.
Two successive test cases are separated by a blank line. A case with N = 0 and M = 0 indicates the end of the input file, and should not be processed by your program.

 

 

Output

For each test case, please print a single integer, the number of distinct common characteristics, to the console. Follow the format as indicated in the sample below.

 

 

Sample Input

5 6

M 0 1

M 1 2

M 1 3

S 1

M 1 2

S 3

 

3 1

M 1 2

 

0 0

 

 

Sample Output

Case #1: 3

Case #2: 2

 

 

Source

2008 Asia Regional Hangzhou

 

题目大意:有n个信封,m个操作,一共有两种操作:

1、M a,b表示a号信封和b号信封属于同一种信封。

2、S a 表示把a号信封孤立出来属于一种信封。

 

思路:对于M a,b操作,直接判定它们为同一种信封merge起来就好。我们还是详细来说明一下S操作,对于孤立出来一个信封,让它独立成为一种类的信封,最直接能够想到的方法就是对其重新编号,例如有这样的操作:

4 4

M 0 1

M 0 2

M 0 3

S 0

这个时候我们直接更改0号为4号的话,我们就能使得达到目的。

 

 如果变成了这样,我们就达到了目标,那么我们如何使得0号节点被4号节点所代替呢?不难想到,我们可以让0号节点虚拟出来一个4号父节点,那是不是直接f【0】=4就行了呢?明显是不可以的,因为根据并查集的特点,这个时候我们find(1)find(2)find(3)都等于4,显然4号变成了所有节点的父节点,并没有孤立成功。

 那么如何解决这个问题呢?我们让0、1、2、3号节点都虚拟出来一个父节点:


 这个时候我们再连接:

M 0 1 

M 0 2

M 0 3

就有:


显然0、1、2、3这个时候父节点都变成了4,也就是说这四个信封属于一种,这个时候我们:

S 0

孤立出0号信封的时候,我们对0号重新虚拟一个父节点,内容就变成了:

这样,我们就有两种信封了,一种是8,一种是4,很巧妙的解决了这个问题,而且接下来如果有其他操作,他们节点之间也不会有影响。


 AC代码:

 

#include<stdio.h>#include<string.h>using namespace std;int vis[1200000];int f[1200000];int find(int a){    int r=a;    while(f[r]!=r)    r=f[r];    int i=a;    int j;    while(i!=r)    {        j=f[i];        f[i]=r;        i=j;    }    return r;}void merge(int a,int b){    int A,B;    A=find(a);    B=find(b);    if(A!=B)    f[B]=A;}int main(){    int n,m;    int kase=0;    while(~scanf("%d%d",&n,&m))    {        if(n==0&&m==0)break;        int cont=n*2;        for(int i=0;i<n;i++)        {            f[i]=i+n;        }        for(int i=n;i<2*n+m+1;i++)        {            f[i]=i;        }        for(int i=0;i<m;i++)        {            char op[5];            scanf("%s",op);            if(op[0]=='M')            {                int x,y;                scanf("%d%d",&x,&y);                merge(x,y);            }            if(op[0]=='S')            {                int x;                scanf("%d",&x);                f[x]=cont++;            }        }        int output=0;        memset(vis,0,sizeof(vis));        for(int i=0;i<n;i++)        {            int tmp=find(i);            if(vis[tmp]==0)            {                vis[tmp]=1;                output++;            }        }        printf("Case #%d: %d\n",++kase,output);    }}














0 0