帶權並查集(及部分習題)
阿新 • • 發佈:2018-12-20
概念:
參考自白皮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
#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; }