(好短!好用!)
点进来都是云里雾里的吧。。。先来介绍一下
冰茶姬(并查集)是个什么玩意?
(这篇博客主要介绍并查集,想知道冰茶姬是啥的出门左转)
并查集,顾名思义,分为“并”和“查”两个部分,是一种树型的数据结构,用于处理一些不相交集合的合并问题。
先从“查”谈起吧。
“查”
不是闰土刺的那一个啦。。
查询当前的两个节点的父亲是否相同,在查询的同时路径压缩。
路径压缩又是什么嘞。。
结合代码了解一下
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]个方块
这样就很容易得出第四个问题的答案,而最后一个问题,用before[x]-count[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]+before[f1];
}
}
只是核心代码啦。。想要经验的话就自己加输入输出好啦。。(小声)
(关于上面的那道很难的题。。我会尽力写啦。。不会鸽掉的)
(RP++!)