加权并查集,就是普通的并查集加了个权值。以题目来举例吧。
HDU 3038 点击打开链接
题意:给你m个区间的区间端点及区间和,一个个的读入这些区间,问有多少个区间与前面的区间冲突。
分析:将区间端点看成是一个节点,用一个parent[i]数组表示结点i指向的父节点,用一个sum[i]数组表示结点i到父节点的权值。运用了前缀和的思想。
代码:
#include<iostream> #include<cstring> using namespace std; const int N = 2e5+5; int n,m,s[N],p[N],ans; void init(){ ans=0; memset(s,0,sizeof(s)); for(int i=0;i<N;i++) p[i]=i; } int fd(int x) { ///此时find不单有查找任务,还有更新距离任务 if(x==p[x]) return x; int t=p[x]; p[x]=fd(p[x]); s[x]+=s[t]; ///记录到根节点的距离,一定要有一个思想,根节点是一个区间的一个端点而不是一个区间,输入的区间被合并成了两个点 return p[x]; } void Union(int a,int b,int num) { int x=fd(a),y=fd(b); if(x==y) { if(s[b]!=s[a]+num) ans++; }else { p[y]=x; s[y]=s[a]+num-s[b]; ///y到x的距离等于a到x的距离+b到a的距离-b到y的距离 } } int main(){ while(cin>>n>>m) { init(); for(int i=0;i<m;i++) { int a,b,c; cin>>a>>b>>c; Union(a-1,b,c); ///等价于Union(a,b+1,c); } cout<<ans<<endl; } }
牛客网 第十四届华中科技大学程序设计竞赛决赛同步赛 A - Beauty of Trees 点击打开链接
题意:给你m个区间的区间端点及区间亦或和,一个个的读入这些区间,问有多少个区间与前面的区间冲突。
分析:此题与上一题差不多,只不过将区间和改成了区间亦或和,原理都是一样的,利用亦或的性质维护一个前缀亦或和。
代码:
#include<iostream> #include<cstring> using namespace std; const int N = 1e5+10; int n,m,s[N],p[N],k,f; void init(){ f=0; for(int i=0;i<N;i++) p[i]=i; memset(s,0,sizeof(s)); } int fd(int x) { if(x==p[x]) return x; int t=p[x]; p[x]=fd(p[x]); s[x]^=s[t]; return p[x]; } void Union(int a,int b,int c) { int x=fd(a),y=fd(b); if(x==y) { if(s[b]!=(s[a]^c)) cout<<k<<endl,f=1; ///亦或这里一定要打括号,亦或的优先级低于!= }else { p[y]=x; s[y]=s[a]^s[b]^c; } } int main(){ while(cin>>n>>m) { init(); for(k=1;k<=m;k++) { int a,b,c; cin>>a>>b>>c; Union(a-1,b,c); } if(!f) cout<<-1<<endl; } }