bzoj3376&&poj1988 [Usaco2004 Open]Cube Stacking 方块游戏 解题报告

题目描述

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]);
    }
  }
}

写在最后

不要以为带权并查集是多么高大上的东西
他只是在并查集的过程中维护了一些数值
就叫做并查集的权值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值