1. 程式人生 > >帶權並查集(及部分習題)

帶權並查集(及部分習題)

概念:

參考自白皮p85例題

模板:

int r[max]    //權值
int find(int x)
{
    if(fa[x]==x)
       return x;
     else
     {
          int t=find(fa[x]);
          r[x]+=r[fa[x]];
          return fa[x]=t;
      }
}

例題:

(1)How Many Answers Are Wrong 題意: 輸入為 a,b,s表示從a到b的和為s,求錯誤語句的數量。 思路: 參考自https://blog.csdn.net/dextrad_ihacker/article/details/51016017

因為輸入較多,所以考慮使用帶權並查集判斷輸入的是否與前面的矛盾; 我們將從a到b的權值賦給a節點,因為題目給的是閉區間,我們又可能遇到從a到b和從b到c合併為從a到c,因為前開後閉區間存在(a,b]+(b,c]=(a,c]的性質,所以使用前開後閉區間即輸入後a–。 權值q[a]表示a到a的根節點的距離。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,ans;
int fa[200010],q[200010];
void ini()
{
	for(int i=0;i<=n;i++)
	   fa[i]=i;
}
int find(int x)
{
	if(fa[x]==x)
	  return x;
	int t=find(fa[x]);
	q[x]+=q[fa[x]];   //得到x到根節點的距離
	                  //因為先遞迴到根節點,所以此時fa[x]已經更新為根節點 
	return fa[x]=t;
}
int main()
{
	int x,y,z;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
    	memset(q,0,sizeof(q));
    	memset(fa,0,sizeof(fa));
    	ini();
        ans=0;
	    for(int i=0;i<m;i++){
	    	scanf("%d%d%d",&x,&y,&z);
	    	x--;
	    	int a=find(x);
	    	int b=find(y);
	    	if(a!=b)
	    	{
	    		fa[b]=a;
	    		q[b]=q[x]-q[y]+z;    //向左更新距離 
	    	}
	    	else if(q[y]-q[x]!=z)    //兩者為同一根時判斷距離是否合法 
	    	  ans++;
	    }
    	cout<<ans<<endl;
    } 
	return 0;
}

(2)食物鏈 題意: 給定兩種共k條資訊,第一種表示x和y屬於同一物種,第二種表示x捕食y; 問給出的k條資訊有多少是和前面的資訊矛盾。 思路: 因為有捕食和被捕食關係,所以並查集應帶有權值,另外開闢兩個陣列表示捕食和被捕食(因為要區分x捕食y和y捕食x,所以使用兩個陣列)

int fa[60100*3];     //相當於三個陣列
int n,k; 
void  ini()
{
	for(int i=0;i<=3*n;i++)
	  fa[i]=i;
}
int find(int x)
{
	if(fa[x]==x)  return x;
	return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
	int a=find(x),b=find(y);
	if(a==b) return;
	else
	  fa[b]=a;
}
bool same(int x,int y)
{
	return  find(x)==find(y);
}
int main()
{
	int ans=0;
	cin>>n>>k;
	int d,x,y;
	memset(fa,0,sizeof(fa));
	ini();
	for(int i=0;i<k;i++)
	{
		scanf("%d%d%d",&d,&x,&y);
		x--;
		y--;
		if(x>=n||y>=n||x<0||y<0)
		{
			ans++;
			continue;
		}
		if(d==1)
		{
			if(!same(x,y+n)&&!same(x,y+2*n))
			{
				unite(x,y);
				unite(x+n,y+n);
				unite(x+2*n,y+2*n);
			}
			else
			  ans++;
		}
		else if(d==2)
		{
			if(x==y)
			{
				ans++;
				continue;
			}
			if(!same(x,y)&&!same(x,y+2*n))
			{
				unite(x,y+n);
				unite(x+n,y+2*n);
				unite(x+2*n,y);
			}
			else
			    ans++;
		}
	}
	cout<<ans<<endl;
	return 0;
}