本题考查的是并查集的删除.

在删除结点的时候要注意这样的问题: 并查集的结构是一棵树,当把某一个结点删去(将其父结点置为自己)时,它的子结点的根就会丢失.因此,在删除某结点的时候,要确定它的所有子结点都已经并到根上了.

解决方法: 为每一个结点加一个虚根,这样每个结点都是叶子结点.插入结点时,把它们都并到虚根的集合中.删除结点时,只要把它的父结点置为一个无用的虚根

http://blog.csdn.net/a181551981/article/details/6286007

#include <iostream>   
#include <string>   
using namespace std;  
int n,m,a,b,cnt,father[2000000],s[1000041],ans;  
int find(int x);
void merge(int x,int y);

//固定根节点,n+1~n+n作为根节点,而1~n作为虚拟根节点(指向n+1~n+n),之后在增添n+n~n+n+m作为备用节点   
//删除时直接修改1~n指向的节点到n+n后的节点   
int main(){  
    int Case = 1, n, m, i, a, b, ans;  
    char ch;
    while(cin>>n>>m && (n||m))
    {  
        for(i = 0; i < n; i++)
            father[i] = i + n;
        for(i = n; i < n*2+m;i++)
            father[i] = i;
        cnt = 2 * n; ans = 0;
        memset(s, 0, sizeof(s));
        while(m--)
        {
            cin>>ch;
            if(ch == 'M')
            {
                cin>>a>>b;
                merge(a, b);
            }
            else
            {
                cin>>a;
                father[a] = cnt++;
            }
        }
        for(i = 0; i < n; i++)
        {
            int temp = find(i);
            if(s[temp] == 0)
            {
                ans++;
                s[temp] = 1;
            }
        }
        cout<<"Case #"<<Case<<": "<<ans<<endl;
        Case++;
    }  
    return 0;     
}  
int find(int x) {
    int temp = x,sum = 0,ans;
    while(temp != father[temp]) {
       temp = father[temp];
    }
    ans = temp;
    while(x != ans) {
       temp = father[x];
       father[x] = ans;
       x = temp;
    }
    return ans;
}

void merge(int a,int b)
{
    int x = find(a);
    int y = find(b);
    if(x < y)
        father[y] = x;
    else if(x > y)
        father[x] = y;
}

results matching ""

    No results matching ""