并查集的复习

蒟蒻刚刚得到了教练给的VB网址便前往刷题:

教练:上面的题都是基础,你们自己好好练吧:

我:好耶!!冲冲!!

one hours later~

我:我去,这题是毒瘤吧,什么鬼并查集呀!

被逼无奈的我在洛谷上找到了题目。一看,蓝题!教练啊,不要虐我这个刚刚躺进复赛的蒟蒻。

好吧,看看大佬的题解,我终于弄明白了这个叫带权并查集的东东;

众所周知:并查集是用来合并集合的好东西,查找也很快但不到O(1)。我们用它来做最小生成树(kruskal)。便于我们判断一条边的两个节点是否存在同一个集合中。

比如说:

Cube Stacking POJ-1988 落谷5092

https://www.luogu.com.cn/problem/P5092

题面吗,自己看看。就是移动堆栈,判断每一个方块下方的方块数量;

思路便是在查询父亲时,利用回溯法,在查找的同时返回每一个节点的dis值,dis数组维护节点下方的方块数量;我觉得和dfs有相似之处,差不多一模一样;

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 int n;
 4 const int N=1e5+10;
 5 int fa[N];
 6 int size[N];
 7 int dis[N];
 8 int find(int x){
 9     if(fa[x]==x){
10         return x;
11     }
12     int father=find(fa[x];//向下层搜索但别慌
13     dis[x]+=dis[fa[x]];//更新dis值
14     fa[x]=father;//返回父亲
15     return father;
16 }
17 void merge(int x,int y){
18     int fx=find(x);
19     int fy=find(y);
20     
21         fa[fx]=fy;//路径压缩
22         dis[fx]+=size[fy];//父亲的dis值加上y所在集合的size
23         size[fy]+=size[fx];//下方集合的方块数++
24     
25     return ;
26 }
27 void query(int x){
28     int ffx=find(x);//看似没用,实则有用目的是更新x的dis值
29     cout<<dis[x]<<endl;
30 }
31 int main()
32 {
33     cin>>n;
34     for(int i=1;i<=n;i++){
35         fa[i]=i;
36         size[i]=1;//初始化
37     }
38     for(int i=1;i<=n;i++){
39         char A;
40         cin>>A;
41         if(A=='M'){
42             int x,y;
43             cin>>x>>y;
44             merge(x,y);
45         }else{
46             int x;
47             cin>>x;
48             query(x);
49         }
50     }
51     return 0;
52 }
复制代码

 

马上便是下一道毒瘤题(对我这种蒟蒻而言)

2.食物链 落谷2024 https://www.luogu.com.cn/problem/P2024

题意就是每次读入两者之间的关系,先特判前两种是假话的情况;

最后一种情况就分两种情况 1.两者是同类;2.两者不是同类;我们只用一个并查集来维护所有动物之间的关系,牛不牛逼~~~~

1.两者是同类,在判断两者是否在一个集合内,如果在一个集合内部,我们就用两个节点与父亲之间的关系来确定这句话是不是假话;如果不在,就合并,并更新关系;

2.两者不是同类,同上;

重点便是:我们怎么更新子节点与父节点之间的捕食关系,又怎样通过父节点来判断两者之间的关系;

接下来就到了最关键的时刻:::倾听大佬(当然不是我)的  讲解 

重点的find函数:

复制代码
 1 int find(int a)
 2 {
 3     int fa=f[a];
 4     if (a!=fa) {
 5         f[a]=find(fa);//找到爸爸先别慌~~
 6         re[a]=(re[a]+re[fa])%3;//完美的取模运算~~~
 7         return f[a];
 8     }
 9     else return fa;
10 }
复制代码

重点的merge函数:

 1 f[f1]=f2; re[f1]=(3-re[a]+re[b])%3;//合并倒推与父亲的关系 又是取模运算~~~~~~~~~

 1 int rela=(re[a]-re[b]+3)%3; //用两个节点与父亲的关系推出两者关系  

全部代码:

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 int f[100000],re[100000];
 4 int n,m,a,b,p,ans=0;
 5 
 6 int find (int a){
 7     int fa=f[a];
 8     if(a!=fa){
 9         f[a]=find(fa);
10         re[a]=(re[a]+re[fa])%3;
11         return f[a];
12     }else return fa;
13 }
14 
15 
16 int main()
17 {    scanf("%d%d",&n,&m);//作为一道毒瘤题怎么能让你用cin呢???
18     for(int i=1;i<=n;i++){
19         f[i]=i;
20         re[i]=0;//自己不可能吃自己,都是同类
21     }
22     for(int i=1;i<=m;i++){
23         scanf("%d%d%d",&p,&a,&b);
24         if((a>n||b>n)||(p==2&&a==b))//特判两种情况
25             ans++;continue;
26         }
27         if(p==1){
28             int f1=find(a),f2=find(b);
29             if(f1==f2&&re[a]!=re[b]){//都是同类了怎么还能与父节点的关系不同呢
30                 ans++;continue;
31             }else if(f1!=f2){
32                 f[f1]=f2;
33                 re[f1]=(3-re[a]+re[b])%3;//膜拜大佬
34                 
35             }
36         }
37         if(p==2){
38             int f1=find(a),f2=find(b);
39             if(f1==f2){
40                 int rela=(re[a]-re[b]+3)%3;//由父亲到儿子
41                 if(rela!=1){
42                     ans++;
43                     continue;
44                 }
45             }
46             else{
47                 int f1=find(a),f2=find(b);
48                 f[f1]=f2;
49                 re[f1]=(3-re[a]+re[b]+1)%3;//以 下 犯 上(~^_^~)
50             }
51         }
52     }
53     cout<<ans;
54     return 0;
55 }
复制代码

今天的复习到此结束,拜拜~~

看在我今天为小妹妹让座的份上,点个赞吧~~~~

 

posted @   SSZX_loser_lcy  阅读(76)  评论(3编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示