学习笔记 带权并查集

 

 

今天的蒟蒻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​​) 间的曼哈顿距离定义为 |x1x2|+|y1y2|。

  输入格式

  第111行:两个分开的整数NNN和MMM.

  第222到M+1M+1M+1行:每行包括4个分开的内容,F1F2LD分别描述两个农场的编号,道路的长度,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部分组成:F1F2J.其中F1F_1F1​​和F2F_2F2​​表示两个被问及的农场.而J(1≤J≤M)J(1\leq J\leq M)J(1JM)表示问题提出的时刻.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 }
View Code

 

 

 

 

 

 

posted @ 2019-08-22 14:36  落筱  阅读(323)  评论(1编辑  收藏  举报