并查集重修寄
并查集重修寄
什么简单的操作就不说了,没啥必要。
带权并查集
奇奇怪怪的东西
就是在两点之前加了个权值,一般代表两者之间的关系。
根据化学上的盖斯定律
一条显然的结论:
两点之间的关系是不变的,无论中间经过多少节点。
代码的话就是:
int find(int x){
if(x!=fa[x]){int t=fa[x]fa[x]=find(fa[x]);val[x]+=val[t];}
return fa[x];
}
原理就是在路径压缩返回的时候,父节点被修改,所以提前存一下父节点,然后递归一路修改上去,就得到了节点到根节点的关系。
合并就是:
int uf=find(u),vf=find(v);
if(uf^vf) fa[uf]=vf,val[uf]=val[v]-val[u]+s;
画图手摸一下显然。
例题
不知名 HIhoCoder 题目。
小 Hi 的学校总共有N名学生,编号 \(1-N\) 。学校刚刚进行了一场全校的古诗文水平测验。
学校没有公布测验的成绩,所以小Hi只能得到一些小道消息,例如 \(X\) 号同学的分数比 \(Y\) 号同学的分数高 \(S\) 分。
小 Hi 想知道利用这些消息,能不能判断出某两位同学之间的分数高低?
输入
第一行包含三个整数 \(N, M\) 和 \(Q\)。\(N\) 表示学生总数,\(M\) 表示小 Hi 知道消息的总数,\(Q\) 表示小 Hi 想询问的数量。
以下 \(M\) 行每行三个整数,\(X, Y和S\) 。表示 \(X\) 号同学的分数比 \(Y\) 号同学的分数高 \(S\) 分。
以下 \(Q\) 行每行两个整数,\(X\) 和 \(Y\)。表示小 Hi 想知道 \(X\) 号同学的分数比 \(Y\) 号同学的分数高几分。
输出
对于每个询问,如果不能判断出 \(X\) 比 \(Y\) 高几分输出 \(-1\)。否则输出 \(X\) 比 \(Y\) 高的分数。
都是相对关系,建立带权并查集即可,权值是子节点与父节点的分数之差。
一看三种关系,同类,吃和被吃。三个并查集莽上去直接种类并查集解决可以。
但是带权并查集本就是处理相对关系的,实现更简单。
权值这么规定:同类为 \(0\),\(A\) 吃 \(B\) 为 \(1\),\(B\) 吃 \(A\) 为 \(2\)
显然这玩意不能像前面一样直接累加,得想想会怎么变化,
我们用数学归纳法?先假定只有 \(A \ B \ C\) 三个点。
- 1.如果 \(A\ B\) 为 \(1\) ,\(B\ C\) 为 \(1\) ,显然是 \(C\) 吃 \(A\) ,\(A \ C\) 应为 \(2\)
- 2.如果 \(A\ B\) 为 \(2\) ,\(B\ C\) 为 \(2\) ,显然是 \(A\) 吃 \(C\) ,\(A \ C\) 应为 \(1\)
- 3.如果 \(A\ B\) 为 \(0\) ,\(B\ C\) 为 \(1\) ,显然是 \(A\) 吃 \(C\) ,\(A \ C\) 应为 \(1\)
- ……
不难发现 \(A \ C= (A\ B+B\ C)\bmod 3\)
同理可得 \(A \ B= (A\ C+C\ B)\bmod 3\) 等,推广到多点,任意三点都满足以上关系。
所以 find 函数和 merge 函数做出相应的更改即可。
判断时直接按照合并的操作算出 \(u,v\) 之间的关系,和利用输入的 \(opt\) 判断即可。
码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;
inline int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
return f?-x:x;
}
int Ans,n,k,val[N],fa[N];
int find(int x){
if(x^fa[x]){int t=fa[x];fa[x]=find(fa[x]);
val[x]=(val[x]+val[t])%3;}return fa[x];
}
signed main() {
n=read();k=read();for(int i=1;i<=n;i++)fa[i]=i;
while(k--){int rel=read(),u=read(),v=read();
if(u>n||v>n||((rel==2)&(u==v)))++Ans;
else{
int uf=find(u),vf=find(v);rel--;
if(uf==vf) if(rel^((val[u]-val[v]+3)%3)) ++Ans;
else fa[uf]=vf,val[uf]=(val[v]-val[u]+rel)%3;
}
}
return printf("%d\n",Ans),0;
}