题目描述
1.bzoj 3376 传送门
2.poj 1988 传送门
想说的话
这道题我很早就做过了 当时就是并查集专题
但是那个时候的我并不知道带权并查集 就是稀里糊涂的抄了题解
昨天刚刚正式学会带权并查集 于是乎我来写一发题解
题解
都说了是带权并查集
而且并查集这个思路也好想 不过就是M到一根柱子上的就合并
这里我们维护三个数值 如下图
father[x]表示x这根柱子上面最上面的方块
d[x]表示x这根柱子上最底下的方块
f[x]表示father[x]到底下的距离(不包括d[x])
当我们把一个x柱放到y柱上面的时候
这个时候我们可以看到d[x]变成了d[y] 显而易见
然后这个时候father[y]的顶端变成了father[x] 也就是father[ father[y] ]=father[x]
这个时候father[y]上面不是没有方块了 而是x这根柱子上面的方块 包括d[x]和d[x]上面的 也就是f[ d[x] ]
写成代码就是 f[ father[y] ]=f[ d[father[x]] ]+1(father[x]与x在一根柱子上 所以d是相同的)
这个时候我们就完成了移动
对于询问也是非常的简单 我们询问x下面有多少
这个时候我们需要的就是画图分析一下 毕竟我们没有这样的一个数组
这个时候我给出了f[x]和f[ d[x] ] 然后可以发现如果用这两个值作差 得到了一段柱子
方块是[x,d[x])左闭右开区间 我们要求的是这个的左开右闭区间 发现它们的数值相等
这样的话就变得很简单
压缩路径大体跟移动没啥太大区别
不进行过多的赘述
代码
#include<cstdio>
#define N 30001
int father[N],d[N],f[N],n;
inline int getroot(int x)
{
if(father[x]!=x)
{
int y=father[x];
father[x]=getroot(father[x]);
f[x]=f[x]+f[y];
d[x]=d[y];
}
return father[x];
}
void init()
{
for(int i=1;i<=30000;i++) father[i]=d[i]=i,f[i]=0;
}
int main()
{
scanf("%d",&n);
init();
for(int i=1;i<=n;i++)
{
char ch=getchar();
while(ch!='M'&&ch!='C') ch=getchar();
if(ch=='M')
{
int x,y;
scanf("%d%d",&x,&y);
int fx=getroot(x),fy=getroot(y);
father[fy]=fx;
int ls=getroot(d[fx]);
f[fy]=f[d[fx]]+1;
d[fx]=d[fy];
}
else
{
int x;
scanf("%d",&x);
int ls=d[getroot(x)];
int oz=getroot(ls);
printf("%d\n",f[ls]-f[x]);
}
}
}
写在最后
不要以为带权并查集是多么高大上的东西
他只是在并查集的过程中维护了一些数值
就叫做并查集的权值