加权并查集,就是普通的并查集加了个权值。以题目来举例吧。
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;
}
}