并查集重修寄

并查集重修寄

什么简单的操作就不说了,没啥必要。

带权并查集

奇奇怪怪的东西

就是在两点之前加了个权值,一般代表两者之间的关系。

根据化学上的盖斯定律

一条显然的结论:

两点之间的关系是不变的,无论中间经过多少节点。

代码的话就是:

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\) 高的分数。


都是相对关系,建立带权并查集即可,权值是子节点与父节点的分数之差。

P2024 [NOI2001] 食物链

一看三种关系,同类,吃和被吃。三个并查集莽上去直接种类并查集解决可以。

但是带权并查集本就是处理相对关系的,实现更简单。

权值这么规定:同类为 \(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;
}
posted @ 2022-05-12 20:31  Gym_nastics  阅读(25)  评论(0编辑  收藏  举报