HDU 2473 JunkMail Filter(并查集)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2473

题意:有0到n-1个数,M a b  表示a和b合并,S a表示删除a,删除以后a变成独立的集合。。。最后问共有几个集合。。。。

开始做的时候是真的按步就搬地把a删除,然后把他儿子和他老爸都合起来。。。结果超时。。。。

还有吧本来是int 型的a,b写成了 char型导致一直溢出,找了很久才找到。。。

这里用了代理index[i]表示i的代理是index[i],然后以后操作的时候每个人干活只找代理干,然后删除的时候直接换个新代理(这个代理不能和别人的代理冲突,开始的时候是独立的集合),

然后他原来的代理事实上还在本来那里,没有删除,因为我们只找代理来干活,而他原来的代理已经不是代理了,所以不会找到原来那个代理。。。。。

最后统计的时候,正常来说我们可以扫一遍代理,看father[代理] == 代理就累加,而这里不正常,会计少了集合的。。。。。

因为当你删除的代理是根,而该树的儿子孙子还有别人的代理,即该集合也算一个集合,当你最后找的时候,因为只找代理,而那些算一个集合的,而根本来是代理,因为删除后而不是代理了的集合就会没有统计。。。。

所以我们扫所有代理用Find()来找根,然后用标记法(因为可能多个代理同一个根)来把根标记,最后统计根的个数

#include <iostream>
using namespace std;

const  int M = 10000100;

int father[M];
int rank1[M];
int save[M];
int index[M];
bool used[M];


void Make_Set(int x)
{
    father[x] = x;
    rank1[x] = 0;
}

int Find(int x)
{
    int k = 0;
    while (x != father[x])
    {
        save[k++] = x;
        x = father[x];
    }

    for (int j = 0; j < k; j++)
    {
        father[save[j]] = x;
    }
    return x;
}


void Union(int a, int b)
{

    int x = Find(a);
    int y = Find(b);
    
    if (x == y)
    {
        return;
    }
    if (rank1[x] < rank1[y])
    {
        father[x] = y;
    }
    else
    {
        father[y] = x;
        if (rank1[x] == rank1[y])
        {
            rank1[x]++;
        }
    }
}



int main()
{
    int n, m;
    int case1 = 0;
    while (~scanf("%d%d", &n, &m), n + m)
    {
        
        for (int i = 0; i < n; i++)
        {
            Make_Set(i);
            index[i] = i;//开始代理都是本身
        }

        int k = n+1;
        while (m--)
        {
            char str[10];
            int a, b;
            scanf("%s", str);
            if (str[0] == 'M')
            {
                scanf("%d%d", &a, &b);
                Union(index[a], index[b]);
            }
            else
            {
                scanf("%d", &a);
                index[a] = k++;//换代理
                Make_Set(index[a]);
            }

        }

        int count = 0;
        memset(used, 0, sizeof(used));
        for (int  i = 0; i < n; i++)
        {
            //if (father[i] == i)或father[index[i]] == index[i] 都是错的
            //{
            //    count++;
            //}
            used[Find(index[i])] = 1;//标记根
        }

        for (int i = 0; i < k; i++)
        {
            if (used[i])
            {
                count++;
            }
        }

        printf("Case #%d: %d\n", ++case1, count);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/qiufeihai/p/2668244.html