地址:http://poj.org/problem?id=1988
Description
Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations:moves and counts.
* In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y.
* In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value.
Write a program that can verify the results of the game.
Input
* Line 1: A single integer, P* Lines 2..P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a 'M' for a move operation or a 'C' for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X.
Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself.
Output
Print the output from each of the count operations in the same order as the input file.Sample Input
6 M 1 6 C 1 M 2 4 M 2 6 C 3 C 4
Sample Output
1 0 2
题意:n个操作。M x y 表示将x所在的那堆箱子放在y那堆箱子头上。C x 询问x下面有多少箱子。
解析一:以deep[x]记录x上面有多少箱子,堆顶为根,以all[x]记录x所在堆有多少个箱子。初始化为deep[]=0,all[]=1,pr[i]=i。find()函数的更新过程(包含路径压缩)
int find(int x) { if(pr[x]==x)return x; int mid=pr[x]; pr[x]=find(pr[x]);//路径压缩 deep[x]+=deep[mid]; //下图记录此步 return pr[x]; }
分析过程:
接下来是join():
void join(int a,int b) { int fa=find(a),fb=find(b); if(fa!=fb) { pr[fb]=fa;//以堆顶为根,a放在了b的头上 deep[fb]+=all[fa];//下图分析此过程 all[fa]+=all[fb];//下图分析此过程 } return ; }
分析过程:
计算当前点下面有多少个箱子,就是:all[find(x)]-deep[x]-1
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<algorithm> using namespace std; typedef long long ll; const int maxn=3e5+50; int n; int pr[maxn],deep[maxn],all[maxn]; void init() { for(int i=1;i<=n;i++) { pr[i]=i; deep[i]=0; all[i]=1; } } int find(int x) { if(pr[x]==x)return x; int mid=pr[x]; pr[x]=find(pr[x]); deep[x]+=deep[mid]; return pr[x]; } void join(int a,int b) { int fa=find(a),fb=find(b); if(fa!=fb) { pr[fb]=fa; deep[fb]+=all[fa]; all[fa]+=all[fb]; } return ; } int main() { while(~scanf("%d",&n)) { init(); char ch; int mid=n; while(mid--) { scanf(" %c",&ch); if(ch=='M') { int a,b; scanf("%d%d",&a,&b); join(a,b); } else { int x; scanf("%d",&x); cout<<all[find(x)]-deep[x]-1<<endl; } } } }
解析二:以deep[x]记录x下面有多少箱子,堆底为根。其他与上面基本相同。find()相同。不同的是join()谁接谁改了一下:
void join(int a,int b) { int fa=find(a),fb=find(b); if(fa!=fb) { pr[fa]=fb;//堆底为根。 deep[fa]+=all[fb];//与上面不同 all[fb]+=all[fa];//与上面不同 } return ; }
询问x,那么find(x)一下,防止之前没更新,直接输出deep[x]即可。
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<algorithm> using namespace std; typedef long long ll; const int maxn=3e5+50; int n; int pr[maxn],deep[maxn],all[maxn]; void init() { for(int i=1;i<=n;i++) { pr[i]=i; deep[i]=0; all[i]=1; } } int find(int x) { if(pr[x]==x)return x; int mid=pr[x]; pr[x]=find(pr[x]); deep[x]+=deep[mid]; return pr[x]; } void join(int a,int b) { int fa=find(a),fb=find(b); if(fa!=fb) { pr[fa]=fb; deep[fa]+=all[fb]; all[fb]+=all[fa]; } return ; } int main() { while(~scanf("%d",&n)) { init(); char ch; int mid=n; while(mid--) { scanf(" %c",&ch); if(ch=='M') { int a,b; scanf("%d%d",&a,&b); join(a,b); } else { int x; scanf("%d",&x); find(x); cout<<deep[x]<<endl; } } } }