HUD 2473 - Junk-Mail Filter

Junk-Mail Filter

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

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.

给定n个点,m个操作。
M a b 表示 a与b属于同一集合
S a 表示 将a从集合删去
所有操作完成后输出集合的个数

Input
There are multiple test cases in the input file.
Each test case starts with two integers, N and M (1 ≤ N ≤ 10 5 , 1 ≤ M ≤ 10 6), 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.
输入包含多组,每组两个整数n,m(1<=n<=100000,1<=n<=1000000)
接下来m行M a b或S a表示操作

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

并查集删点。
建立一个 f 数组来做并查集,建立一个 replace 数组来存代替的点。
f 和 replace 数组一开始都指向自身。
下面说明一下怎么用replace来代替点。

比如一共5个点,1为0,2,3的根节点,然后我们S 1,f 数组不动,replace[1]的值从 1 变成 5。然后接下来的和 点1 有关的操作 和 replace[1] (也就是新创造出来的点5)进行,0,2,3还是以1为根节点。

#include<iostream>
using namespace std;
#include<set>

int f[1000010] = {};//f 数组用来做并查集 
int replace[1000010] = {};//replace 数组用来存代替被替换的点 

int find(int x)
{
    int t = x;
    while (f[x] != x) x = f[x];
    while (t != f[t])
    {
        int temp = f[t];
        f[t] = x;
        t = temp;
    }
    return x;
}

void insert(int x,int y)
{
    int fx = find(x);
    int fy = find(y);
    if (fx != fy)
    {
        f[fy] = fx;
    }
}

int main()
{
    int count = 0,n,m;
    while (scanf("%d%d",&n,&m))
    {
        if (n == 0 && m == 0) break;
        for (int i = 0;i<=n-1;i++)//初始化,一开始都指向自身。 
        {
            f[i] = i;
            replace[i] = i;
        }
        char c;
        int v,u,len = n;//新的结点 
        for (int i = 1;i<=m;i++)
        {
            getchar();
            scanf("%c",&c);
            if (c == 'M')
            {
                scanf("%d%d",&u,&v);
                insert(replace[u],replace[v]);
            }
            else
            {
                scanf("%d",&u);
                f[len] = len;//新建一个点并且作为根节点,也就是指向自身 
                replace[u] = len++;//把删除的点指向新的点。接下来的和旧的点有关操作都由这个新的点来代替。 
            }
        }
        int num = 0;
        set<int> s;
        for (int i = 0;i<=n-1;i++)
        {
            int t = find(replace[i]);
            if (s.count(t) == 0)
            {
                num++;
                s.insert(t);
            }
        }
        printf("Case #%d: %d\n",++count,num);
    }
}

猜你喜欢

转载自blog.csdn.net/EIP_silly/article/details/88075095
HUD