并查集重修寄

并查集重修寄

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

带权并查集

奇奇怪怪的东西

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

根据化学上的盖斯定律

一条显然的结论:

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

代码的话就是:

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  Shufen_233  阅读(79)  评论(0)    收藏  举报