学习笔记 带权并查集
今天的蒟蒻Lbmttw_lx又要学习新的有趣的东西了!
.
最近考的NOIP基础知识里面涉及到了一个比较有趣的东西,叫做带权并查集,可是Lbmttw_lx并不知道是什么,所以今天痛下决心好好学一下
正文分割线
理解权值并查集我们首先需要知道并查集是什么,在这里简单说一下吧
普通并查集的主要作用是判断图是否连通,即两个节点之间是否存在某种联系(即同一个根节点),如下图所示就是一个很普通的并查集,只能看到边代表着方向额而没有记录其他信息
代码应该都会写,不上了
但是有些时候我们需要判断更多的信息,比如把多个连通的图合并为一个图并且记录合并后节点的个数,我们就需要一个更为高级的结构,权值并查集
如何理解这个权值呢?这个权值的意思就是需要额外记录一些信息。
基于对并查集的理解,我们会发现一个比较有趣的事情,就是
看上面的图,我们对C进行find操作,让c连上A,不过在此之前,C会先找到B,然后才连上A,我们可不可以直接把C和A相连呢,这样极大程度上缩小了时间复杂度,得到的结果如下
这种优化有一个学名,叫做并查集的路径压缩,将每一个节点直接与其find操作得到的节点连接。
很轻松的可以写出这样的代码
1 int find(int x) 2 { 3 if (x != f[x]) 4 { 5 f[x] = find(f[x]); 6 } 7 return f[x]; 8 }
和一般的并查集相比,这个不过是多了一个在find前面加了一个赋值操作,把所有要找的节点的根节点设成最后找到的那个点而已!!!但是就比之前的传统并查集优秀
基于路径压缩的原理,权值并查集就变成了一下的样子
发现这个图,每一条边都记录了一条信息,具体记录什么信息需要依据题意而定,in general 都是一些边的相对关系。但是最主要的是,有权值就会有几个问题
1.由于我们优秀的路径压缩操作,所以我们记录的权值都是与根节点之间的权值,在我们find操作中,路径压缩也要更新这个权值
2.在并查集的合并操作中,我们也需要更新权值,因为根节点!!!路径压缩存储的是与根节点之间的权值!!!
所以呢,路径压缩的权值修改和并查集合并操作更新权值代码部分如下
(合并并查集)
1 x=read(),y=read(); 2 int fx=find(x); 3 int fy=find(y); 4 if(fy!=fx) 5 { 6 f[fx]=fy; 7 sum[fx]=num[fy]; 8 num[fy]+=num[fx]; 9 }
(路径压缩权值修改)
1 int find(int x) 2 { 3 if(x==f[x]) 4 return x; 5 int fa=find(f[x]); 6 sum[x]+=sum[f[x]]; 7 return f[x]=fa; 8 }
是不是非常简单呢??
23333上两道例题
题目描述
约翰所在的乡村可以看做一个二维平面,其中有 NNN 座牧场,每座牧场都有自己的坐标,编号为 111 到 NNN。牧场间存在一些道路,每条道路道路连接两个不同的牧场,方向必定平行于 XXX 轴或 YYY 轴。乡下地方的道路不会太多,连通两座牧场之间的路径是唯一的。
突然间,约翰的导航仪失灵了,牧场的坐标记录全部消失了。所幸的是,约翰找到了表示道路的数据,可以通过这些信息得知牧场间的相对位置。但贝西有急事,在约翰工作到一半的时候就要知道一些牧场间的曼哈顿距离。这时,如果约翰能从找回的道路信息之间推算出答案,就会告诉贝西。请你帮助约翰来回答贝西的问题吧。(x1,y1) (x_1, y_1)(x1,y1) 和 (x2,y2)(x_2, y_2)(x2,y2) 间的曼哈顿距离定义为 |x1−x2|+|y1−y2|。
输入格式
第111行:两个分开的整数NNN和MMM.
第222到M+1M+1M+1行:每行包括4个分开的内容,F1,F2,L,D分别描述两个农场的编号,道路的长度,F1F_1F1到F2F_2F2的方向N,E,S,WN,E,S,WN,E,S,W.
第M+2M+2M+2行:一个整数,KKK,表示问题个数.
第M+3M+3M+3到M+K+2M+K+2M+K+2行:每行表示一个问题,由3部分组成:F1,F2,J.其中F1F_1F1和F2F_2F2表示两个被问及的农场.而J(1≤J≤M)J(1\leq J\leq M)J(1≤J≤M)表示问题提出的时刻.JJJ为111时,表示得知信息111但未得知信息222时.
输出格式
• 对每个询问,输出牧场间的曼哈顿距离,如果当时恢复的数据还不足以得知答案,输出 −1。以换行符分隔答案
样例
样例输入 1
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6 1
1 4 3
2 6 6
样例输出 1
13
-1
10
数据范围与提示
样例解释1
在时刻1,约翰知道1到6的距离为13;
在时刻3,1到4的距离仍然不知道;
在时刻6,位置6向北3个距离,向西7个距离于位置2,所以距离为10.
AC代码如下
1 #include <bits/stdc++.h> 2 #define ll long long 3 #define res register 4 #define MAXN 100050 5 using namespace std; 6 int f[MAXN],nx[MAXN],ny[MAXN]; 7 int n,m,q; 8 char s[10]; 9 int find(int x) 10 { 11 int y=f[x]; 12 if(x!=f[x]) 13 f[x]=find(f[x]); 14 nx[x]+=nx[y]; 15 ny[x]+=ny[y]; 16 return f[x]; 17 } 18 struct Node 19 { 20 int x,y,c,p; 21 int ans; 22 inline friend bool operator <(Node a,Node b) 23 { 24 return a.c<b.c; 25 } 26 }t[40010],tt[50010]; 27 bool cmp(Node a,Node b) 28 { 29 return a.p<b.p; 30 } 31 ll read() 32 { 33 int s=0,w=1;char ch=getchar(); 34 while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} 35 while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-48;ch=getchar();} 36 return s*w; 37 } 38 int main() 39 { 40 freopen("navigate.in","r",stdin); 41 freopen("navigate.out","w",stdout); 42 n=read(),m=read(); 43 for(res int i=1;i<=n;i++) 44 f[i]=i,nx[i]=ny[i]=0; 45 for(res int i=1;i<=m;i++) 46 { 47 int x,y,c; 48 x=read(),y=read(),c=read(); 49 scanf("%s",s); 50 t[i].x=x; 51 t[i].y=y; 52 t[i].c=c; 53 if(s[0]=='E') 54 t[i].p=0; 55 else if(s[0]=='W') 56 t[i].p=1; 57 else if(s[0]=='N') 58 t[i].p=2; 59 else 60 t[i].p=3; 61 } 62 q=read(); 63 for(res int i=1;i<=q;i++) 64 { 65 int x,y,c; 66 x=read(),y=read(),c=read(); 67 tt[i].x=x; 68 tt[i].y=y; 69 tt[i].c=c; 70 tt[i].p=i; 71 } 72 sort(tt+1,tt+1+q); 73 int now=0; 74 for(int i=1;i<=q;i++) 75 { 76 while(now<tt[i].c) 77 { 78 now++; 79 int x=t[now].x,y=t[now].y,c=t[now].c; 80 int ff=find(y); 81 nx[ff]=-nx[y]; 82 ny[ff]=-ny[y]; 83 f[ff]=y; 84 nx[y]=ny[y]=0; 85 f[y]=x; 86 if(t[now].p==0) 87 nx[y]=c; 88 else if(t[now].p==1) 89 nx[y]=-c; 90 else if(t[now].p==2) 91 ny[y]=c; 92 else 93 ny[y]=-c; 94 } 95 int x=tt[i].x,y=tt[i].y; 96 if(find(x)!=find(y)) tt[i].ans=-1; 97 else 98 { 99 tt[i].ans=abs(nx[x]-nx[y])+abs(ny[x]-ny[y]); 100 } 101 } 102 sort(tt+1,tt+1+q,cmp); 103 for(int i=1;i<=q;i++) 104 printf("%d\n",tt[i].ans); 105 return 0; 106 }