Description
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
* 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
Sample Input
6 M 1 6 C 1 M 2 4 M 2 6 C 3 C 4
Sample Output
1 0 2
一道关系并查集题目,做了几道后现在有点感觉了。题目意思就是有两种操作:M x,y是将含x的堆放在含y的堆上面,C x操作时输出x下面立方体的数目。
其实这类题目就是找点的权值之间的规律。首先我们需要赋予点一个有意义的值(如此题的两种做法,一是把点上方的数目当成权值一是把下方的数目当初权值),然后find函数负责更新这些权值,使更新后的每个点的权值都是正确的。然后Union函数负责使每个头节点的点得到更新,这样再一次调用find函数时根据头节点的更新值每个下属节点也会得到更新。
好的,多的不说了,通过题目了解知识是很好的途径,接下来看看这题吧。
这题有两种做法:
一是每次结合时(M X Y),把节点上面的节点数当成点的权值up[i],并且记录每个堆的总数目cnt[i],最后使用总数目减去权值即为所求,这种做法结合时将Y指向X,即上面的堆为头节点。
主要代码:
int find(int x)
{
int y;
if(x!=front[x])
{
y=front[x];
front[x]=find(y);
up[x]+=up[y]; //更新X节点以上的节点数目
}
return front[x];
}
就是在普通find函数里面增加了一个up数组的更新。因为每个立方体上面的立方体总数=立方体到其根节点的立方体数目加根节点上面的立方体数目。
void Union(int x,int y)
{
int px=find(x);
int py=find(y);
if(px==py)
return ;
front[py]=px; //将下面的立方体的根节点指向上面的立方体的根节点
up[py]=cnt[px]; //这时下面立方体的根节点上面的立方体数=上面立方体的总数
cnt[px]+=cnt[py]; //更新合并后堆得根节点之下的总数
}
如果还不清楚,举个例吧:
现在有两堆立方体
1
2 5
3 6
4 7
如果执行 M 3,6操作。那么我们将堆的根节点5——》1,这时up[5]被更新成了4,第一堆立方体不变,第二堆5以下的立方体也暂时不变。现在两堆变成了一堆,根节点为1,它之下的总节点数也被更新成了3+4=7。
合并后如下:
1 《—— 5
2 6
3 7
4
然后执行C 6操作。使用find(6),更新后up[6]=6到5的立方体数加上5之上的立方体数=5。最后用cnt[1]-up[6]-1=1得答案。
具体代码如下:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
int front[30010],cnt[30010],up[30010];
int find(int x)
{
int y;
if(x!=front[x])
{
y=front[x];
front[x]=find(y);
up[x]+=up[y];
}
return front[x];
}
void Union(int x,int y)
{
int px=find(x);
int py=find(y);
if(px==py)
return ;
front[py]=px;
up[py]=cnt[px];
cnt[px]+=cnt[py];
}
int main()
{
int p,i,j,x,y;
char a;
cin>>p;
for(i=1;i<=30000;i++)
{
front[i]=i;
cnt[i]=1;
up[i]=0;
}
while(p--)
{
cin>>a;
if(a=='M')
{
scanf("%d%d",&x,&y);
Union(x,y);
}
else
{
scanf("%d",&x);
int px=find(x);
printf("%d\n",cnt[px]-up[x]-1);
}
}
return 0;
}
第二种办法是放在上面的根节点指向下面的根节点,这样就可以每次更新每个节点之下的节点数below[x]了,原理与第一种办法差不多,代码如下:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
int front[30010],cnt[30010],dis[30010];
int find(int x)
{
int y;
if(x!=front[x])
{
y=front[x];
front[x]=find(front[x]);
dis[x]+=dis[y];
}
return front[x];
}
void Union(int x,int y)
{
int px=find(x);
int py=find(y);
if(px==py)
return ;
front[px]=py;
dis[px]+=cnt[py];
cnt[py]+=cnt[px];
}
int main()
{
int p,i,j,x,y;
char a;
cin>>p;
for(i=1;i<=30000;i++)
{
front[i]=i;
dis[i]=0;
cnt[i]=1;
}
while(p--)
{
cin>>a;
if(a=='M')
{
scanf("%d%d",&x,&y);
Union(x,y);
}
else
{
scanf("%d",&x);
find(x);
cout<<dis[x]<<endl;
}
}
return 0;
}