LG-P2342 叠积木

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xu0_zy/article/details/83381347

P2342 叠积木
题目链接
题目背景
Cube Stacking, 2004 Open

题目描述
约翰和贝西在叠积木。共有30000块积木,编号为1到30000。一开始,这些积木放在地上,自然地分成N堆。贝西接受约翰的指示,把一些积木叠在另一些积木的上面。一旦两块积木相叠, 彼此就再也不会分开了,所以最后叠在一起的积木会越来越高。约翰让贝西依次执行P条操作,操作分为两种:

? 第一种是移动操作,格式为“移动X到Y的上面”。X和Y代表两块积木的编号,意思是将X所的那堆积木,整体叠放到Y所在的那堆积木之上;

? 第二种是统计操作,格式为“统计Z下方的积木数量”。Z代表一块积木的编号,意思是贝西需要报告在编号为Z的积木之下还有多少块积木

请编写一个程序,帮助贝西回答每条统计问题。

输入格式:
? 第一行:单个整数:P,1 ≤ P ≤ 10^5

? 第二行到第P + 1行:每行描述一条命令,如果这行开头的字母是 M,代表一条移动命令,后面的两个整数代表上文中的X和Y;如果开头字母是 C,代表一条统计命令。后面的整数代表上文中的Z,保证所有的移动命令都有意义,X和Y不会已经出现在同一堆积木里

输出格式:
? 对每一个统计命令,输出正确回答,用换行符分开每个查询的结果

输入样例:
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
输出样例:
1
0
2
说明
第一次查询时, 1 下面只有一个 6;第二次查询时, 3 下面没有任何积木;第三次查询时,4 下面有两块积木:1 和 6

题解
真不知道洛谷题时是不是没人会加LG前缀

这题最容易想到的就是利用并查集来求。

用C数组表示每个箱子下面的箱子个数。
我们不可能每次移动后去更新每个箱子。
显然,那样效率太低了。

解决方案之一就是每次推的时候(即刷并查集的时候)修正。

想象一下(或者打开你的mspaint.exe),如果现在我们把一个积木 1 移动到另一个积木 2 上面,我们应该把谁设为父节点?

经过尝试你会发现,如果把 1 设为父节点,也就是把最顶上的设为父节点。
假设 1 为这堆最顶上的,2 为这堆最底下的。
那么当你把这堆东西放到另一堆东西上时,你会发现 2 以下下的积木数量修正以后无法传递给上面的积木。(路径压缩后的并查集是多叉树,难以继续修正)
而询问 2 上面积木的时候也不知道该通过谁来修正一趟。

所以,我们还是把最底层的设为根节点。

接下来,我们考虑一下怎么修正。

如果把 1 移到以 2 为堆底的那堆上面,又把 2 移动到其他东西上面,此时我们再求 1 的父节点时的便利过程:
1->2->fa[2]->……end,最终 1 的父节点会被修正成 end。
(压缩路径后为多叉树)
我们把一棵以 2 为根树接到一棵以 end 为根的树上(接在end上),然后需要在压缩路径的同时修正答案。
(2 的答案以及修正了)

我们从 2 的一个子节点开始看,可以发现我们只需把 2 的答案叠加给它就好了。
因为 c[2] 原本为 0(因为它是堆底,下面不可能有东西),这样也保证了我们重复求一个点祖先的时候不会把答案叠加重复。

那么,如果是 2 的儿子的儿子呢?
既然它不是 2 的直属,那么说明这条路径没压缩过。假设 2 有个儿子 x,这个 x 也有个儿子 y,显然,y 的祖先从 x 变成 2 的时候,也并没有把增加的代价叠加过来,而增加的代价正好是 x 当前的值。

真妙啊,此时会发现,或许给 C 数组换一个定义就可以更好地理解。

C[x] 表示:节点 x 的子节点的懒惰标记。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e4+5;
int n=3e4,m,fa[maxn],ans[maxn],H[maxn];
int rad()
{
	int ret=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
int get(int x)
{
	return x!=fa[x]?get(fa[x]),ans[x]+=ans[fa[x]],fa[x]=fa[fa[x]]:x;
}
int main()
{
	m=rad();
	for (int i=1;i<=n;++i) fa[i]=i,ans[i]=0,H[i]=1;
	char ch;
	for (int i=1;i<=m;++i)
	{
		cin>>ch;
		if (ch=='M')
		{
			int x=get(rad()),y=get(rad());
			if (x==y) continue;
			fa[x]=y;
			ans[x]+=H[y];
			H[y]+=H[x];
		}else
		{
			int x=rad();
			get(x);
			printf("%d\n",ans[x]);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/83381347