带权并查集
简介
带权并查集是一种在维护 $f$ 数组的同时维护一个新数组,代表当前点到根的距离和。
A 两个团伙
题意
告诉你两个操作:
- 表示
x y不是同一个团伙
- 问你
x y是否是一个团伙
解法
如果两人不是同一团伙,就合并,并且连一条权值为 $1$ 的边,更新点到根的和。
查询就看点到根的距离是否能被 $2$ 整除,能就说明是同一个团伙,否则不是。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <bits/stdc++.h> using namespace std;
const int N = 1000010; int f[N],sum[N]; int n,m; int t; int find(int x) { if(x==f[x]) return x; int root=find(f[x]); sum[x]^=sum[f[x]]; return f[x]=root; } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) f[i]=i,sum[i]=0; for(int i=1;i<=m;i++) { char op; int x,y; scanf(" %c %d%d",&op,&x,&y); int tx=find(x),ty=find(y); if(op=='D') { if(sum[x]^sum[y]) sum[tx]=0; else sum[tx]=1; f[tx]=ty; } else { if(find(x)!=find(y)) puts("Not sure yet."); else { if(sum[x]!=sum[y]) puts("In different gangs."); else puts("In the same gang."); } } } } }
|
B区间和探测
题意
每输入一组数据 l r v 表示区间 l r 之和为 v 。
如果这组数据与前面的数据没有冲突,就统计,否则无视。
问有冲突的数据组数。
思路
这道题的 l r 我们可以转换成 $sum[r] - sum[l-1] = v$ ,同时用带权并查集维护。
那么我们可以维护一个并查集,用每个点到根的距离来判定,比如如果我们要合并 l r ,我们就让他到根的权值 $sum_{x-1},sum_y$ 相减为 $z$。
那么假设我们要合并 $x$,$y$,我们就需要让 $sum_y - sum_{x-1} = z$,假设当前 $y$ 的祖先是 $ty$,那么,$sum_{ty} = sum_x + z - sum_y$,就相当于向 $x-1$ 的祖先 $tx$ 连了一条有权的边,可以使得 $sum_y - sum_{x-1} = z$。
同理,如果发现 $x,y$ 已经合并过了,但 $sum_y - sum_{x-1} \ne z$ ,说明有矛盾了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <bits/stdc++.h> using namespace std;
const int N = 4440010; int fa[N],sum[N]; int n,m;
int find(int x) { if(x==fa[x]) return x; int root=find(fa[x]); sum[x]+=sum[fa[x]]; return fa[x]=root; } int main() { scanf("%d%d",&n,&m); int ans=0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); x--; int tx=find(x),ty=find(y); if(tx==ty && sum[y]-sum[x]!=z) ans++; else { fa[ty]=tx; sum[ty]=sum[x]+z-sum[y]; } } printf("%d",ans); }
|