【模板】冰茶姬(大概是全的?)
(好短!好用!)
点进来都是云里雾里的吧。。。先来介绍一下
冰茶姬(并查集)是个什么玩意?
(这篇博客主要介绍并查集,想知道冰茶姬是啥的出门左转)
并查集,顾名思义,分为“并”和“查”两个部分,是一种树型的数据结构,用于处理一些不相交集合的合并问题。
先从“查”谈起吧。
“查”
不是闰土刺的那一个啦。。
查询当前的两个节点的父亲是否相同,在查询的同时路径压缩。
路径压缩又是什么嘞。。
结合代码了解一下
int Getfather(int a) { if(F[a]==a) return a;//如果当前节点的是根节点的话就返回 else F[a]=Getfather(F[a]);//若不是,就将他的父亲一层一层的向上找,知道找到最上面的根为止 (路径压缩) return F[a]; }
这种是最简单的并查集啦。。还会有带权的。。会结合题目一起写在后面
“并”
当两个节点的父亲不同时,合并这两个节点。
结合代码看看
int find(int a,int b) { int f1,f2; f1=Getfather(a); f2=Getfather(b); if(a==b) return 1; else { F[f1]=f2;//合并(你没有看错) return 0; } }
(好简单。。)
看道题:(点击收获三倍经验)这个真是特别裸。。
模板如下:
#include<iostream> using namespace std; int F[50005]; int GF(int x) { if(x==F[x]) { return x; } else { F[x]=GF(F[x]); return F[x]; } } void Merge(int x,int y) { int fx=GF(x); int fy=GF(y); if(fx==fy) return; else { F[fx]=fy; return; } } int n,m,q; int main() { cin>>n>>m>>q; int x,y; for(int i=1;i<=n;i++) F[i]=i; for(int i=1;i<=m;i++) { cin>>x>>y; Merge(x,y); } for(int i=1;i<=q;i++) { cin>>x>>y; if(GF(x)==GF(y)) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }
(但是真正比赛的时候肯定不会这么裸啊。。)
再来一道:(冰茶姬,你值得拥有) 这个其实也裸。。
AC代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int F[50001]; int GF(int x) { if(F[x]==x) return x; F[x]=GF(F[x]); return F[x]; } int main() { int n,m; char A; int B,C; scanf("%d%d",&n,&m); for(int i=1;i<=n*2;i++) F[i]=i; for(int i=1;i<=m;i++) { cin>>A; scanf("%d%d",&B,&C); if(A=='F') F[GF(B)]=F[GF(C)]; if(A=='E') { F[GF(B+n)]=F[GF(C)]; F[GF(C+n)]=F[GF(B)]; } } int Ans=0; for(int i=1;i<=n;i++) { if(F[i]==i) Ans++; } printf("%d",Ans); return 0; }
来一道不那么裸的吧。。。俄罗斯方块++版
这时直接用上面的模板就有问题了。。要怎么统计方块数呢?直接用路径压缩的话就会直接把方块数压缩掉。。。
先想这么几个问题
怎么判断x,y是否在一个柱子上?
怎么合并x,y所在的柱子?
怎么计算x之前的方块个数?
怎么计算x所在柱子的总方块数?
很明显,前两个直接用上文的模板就可以直接搞定,后面两个就不行了。。只用一个Father数组是解决不了这么多问题的。。
我们不妨设x到x的根节点一共有before[x]个方块,x所在的柱子一共有count[x]个方块
这样就很容易得出第四个问题的答案,而最后一个问题,用count[x]-before[x]-1也可以解决(这个地方看不懂就自己画个图,秒懂)
这其实就是较简单的带权并查集啦。。
(还有一题比较难的。。等我看懂了再单独写一篇题解吧。。)
下面是代码:
int Getfather(int a) { int dad; if(F[a]==a) return a;//如果当前节点的是根节点的话就返回 else { dad=Getfather(F[a]); before[a]+=before[F[a]];//将当前节点到根节点的路径累加起来 F[a]=dad; return F[a]; } } void find(int a,int b) { int f1,f2; f1=Getfather(a); f2=Getfather(b); if(a==b) return; else { F[f2]=f1;//f1放在f2的上方,将f2合并到f1所在的集合 count[f1]=count[f2]+count[f1]; before[f2]=before[f2]+count[f1]; } }
只是核心代码啦。。想要经验的话就自己加输入输出好啦。。(小声)
(关于上面的那道很难的题。。我会尽力写啦。。不会鸽掉的)
(RP++!)