hdu2473 Junk-Mail Filter 并查集的删除

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

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

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

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;
}

你可能感兴趣的:(hdu2473 Junk-Mail Filter 并查集的删除)