Problem 洛谷P2342-叠积木
Accept: 373 Submit: 1.1k
Time Limit: 1000 mSec Memory Limit : 128MB
Problem Description
约翰和贝西在叠积木。共有30000块积木,编号为1到30000。一开始,这些积木放在地上,自然地分成N堆。贝西接受约翰的指示,把一些积木叠在另一些积木的上面。一旦两块积木相叠, 彼此就再也不会分开了,所以最后叠在一起的积木会越来越高。约翰让贝西依次执行P条操作,操作分为两种:
- 第一种是移动操作,格式为“移动X到Y的上面”。X和Y代表两块积木的编号,意思是将X所的那堆积木,整体叠放到Y所在的那堆积木之上;
- 第二种是统计操作,格式为“统计Z下方的积木数量”。Z代表一块积木的编号,意思是贝西需要报告在编号为Z的积木之下还有多少块积木;
请编写一个程序,帮助贝西回答每条统计问题。
Input
第一行:单个整数:P,1 ≤ P ≤ 10^5
第二行到第P + 1行:每行描述一条命令,如果这行开头的字母是 M,代表一条移动命令,后面的两个整数代表上文中的X和Y;如果开头字母是 C,代表一条统计命令。后面的整数代表上文中的Z,保证所有的移动命令都有意义,X和Y不会已经出现在同一堆积木里
Output
对每一个统计命令,输出正确回答,用换行符分开每个查询的结果
Sample Input
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
Sample output
1
0
2
题目链接:https://www.luogu.org/problemnew/show/P2342
题解:感觉这个题和一般的带权并查集有那么一点点区别,记录下来。
每个集合的父节点是一堆积木的最下面的积木,权值是当前积木到所在集合父节点的积木个数。
两点需要注意的,一是集合内部关系更改,也就是rel数组,这个是在路径压缩的过程中递归实现的,二是集合之间关系的更改,不仅需要rel数组,还要利用Size数组(记录每个集合当前的大小),当把一个集合放在另一个集合上面时,上面积木的底部到下面积木的底部的积木个数就是Size[下面积木]。
别忘了当一堆积木放在别的积木上面的时候,Size要修改为0。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 using namespace std; 6 7 const int maxn = 30000+10; 8 9 int rel[maxn],pre[maxn]; 10 int Size[maxn]; 11 12 int findn(int x){ 13 if(x == pre[x]) return x; 14 int temp = pre[x]; 15 pre[x] = findn(pre[x]); 16 rel[x] += (rel[temp]); 17 return pre[x]; 18 } 19 20 void merge_node(int x,int y){ 21 int fx = findn(x); 22 int fy = findn(y); 23 if(fx != fy){ 24 pre[fx] = fy; 25 rel[fx] = Size[fy]; 26 Size[fy] += Size[fx]; 27 Size[fx] = 0; 28 } 29 } 30 31 int main() 32 { 33 //freopen("input.txt","r",stdin); 34 int p,n,x,y; 35 char ch[10]; 36 n = 30005; 37 for(int i = 0;i < n;i++){ 38 pre[i] = i; 39 rel[i] = 0; 40 Size[i] = 1; 41 } 42 scanf("%d",&p); 43 for(int i = 1;i <= p;i++){ 44 scanf("%s",ch); 45 if(ch[0] == 'M'){ 46 scanf("%d%d",&x,&y); 47 merge_node(x,y); 48 } 49 else{ 50 scanf("%d",&x); 51 findn(x); 52 printf("%d ",rel[x]); 53 } 54 } 55 return 0; 56 }