Building Block
Problem Description
John are playing with blocks. There are N blocks (1 <= N <= 30000) numbered 1...N。Initially, there are N piles, and each pile contains one block. Then John do some operations P times (1 <= P <= 1000000). There are two kinds of operation:
M X Y : Put the whole pile containing block X up to the pile containing Y. If X and Y are in the same pile, just ignore this command.
C X : Count the number of blocks under block X
You are request to find out the output for each C operation.
M X Y : Put the whole pile containing block X up to the pile containing Y. If X and Y are in the same pile, just ignore this command.
C X : Count the number of blocks under block X
You are request to find out the output for each C operation.
Input
The first line contains integer P. Then P lines follow, each of which contain an operation describe above.
Output
Output the count for each C operations in one line.
Sample Input
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
Sample Output
1
0
2
!!!错误代码
#include<stdio.h>
#include<memory.h>
#include<iostream>
using namespace std;
int T;
int pre[30005];
int a,b,c;
char s[3];
int dis[30005];
int num[30005];
int find(int x){
int r=x;
while(pre[r]!=r){
r=pre[r];
}
int i=x;
int j;
while(pre[i]!=r){
j=pre[i];
pre[i]=r;
dis[i]+=dis[j];
i=j;
}
return r;
}
void join(int x,int y){
int rx=find(x);
int ry=find(y);
if(rx!=ry){
pre[ry]=rx;
dis[ry]=num[rx]+1;
num[rx]=num[rx]+num[ry]+1;
num[ry]=0;
}
}
int main(){
scanf("%d",&T);
for(int j=1;j<=30000;j++){
pre[j]=j;
}
memset(dis,0,sizeof(dis));
memset(num,0,sizeof(num));
while(T--){
scanf("%s",&s);
if(s[0]=='M'){
scanf("%d%d",&a,&b);
join(a,b);
}
else if(s[0]=='C'){
scanf("%d",&c);
printf("%d\n",num[find(c)]-dis[c]);
}
}
return 0;
}
find函数出错,当测试样例为一下所示时:
M 1 2
M 5 6
M 1 5
M 7 1
此时积木叠放次序从上往下为 7 1 2 5 6
若此时执行C 6或 C 5则会出错,因为find函数采用迭代方式,只能从下往上更新,因此应该改为递归,实现从上往下更新
正确代码如下:
#include<stdio.h>
#include<memory.h>
#include<iostream>
using namespace std;
int T;
int pre[30005];
int a,b,c;
char s[3];
int dis[30005];//与根节点的距离,从根节点的积木(不包含)往下数第几块
int num[30005];//以此为根节点的树的子节点个数, 此积木(上面肯定没有积木)下面所有积木的块数
int find(int x){
//递归实现从上往下更新
if(pre[x]==x)return x;
else{
int r=find(pre[x]);
if(pre[x]!=r){
dis[x]+=dis[pre[x]]; //更新与根结点的距离 ,即更新各个积木与最上方积木的距离
pre[x]=r;//路径压缩
}
return pre[x];
}
}
void join(int x,int y){
//若x、y不属于同一组积木,则将y结点所在的树接在x结点所在的树下方
int rx=find(x);
int ry=find(y);
if(rx!=ry){
pre[ry]=rx;
dis[ry]=num[rx]+1;
num[rx]=num[rx]+num[ry]+1;
}
}
int main(){
scanf("%d",&T);
for(int j=1;j<=30000;j++){
pre[j]=j;
}
memset(dis,0,sizeof(dis));
memset(num,0,sizeof(num));
while(T--){
scanf("%s",&s);
if(s[0]=='M'){
scanf("%d%d",&a,&b);
join(a,b);
}
else if(s[0]=='C'){
scanf("%d",&c);
printf("%d\n",num[find(c)]-dis[c]);
//每块积木下方积木数量=最上面的积木下方积木数量 -每块积木与最上面积木之间的距离
}
}
return 0;
}
#include<stdio.h>
#include<memory.h>
#include<iostream>
using namespace std;
int T;
int pre[30005];
int a,b,c;
char s[3];
int dis[30005];
int num[30005];
int find(int x){
if(pre[x]==x)return x;
else{
int r=find(pre[x]);
if(pre[x]!=r){
dis[x]+=dis[pre[x]];
pre[x]=r;
}
return pre[x];
}
}
void join(int x,int y){
int rx=find(x);
int ry=find(y);
if(rx!=ry){
pre[ry]=rx;
dis[ry]=num[rx]+1;
num[rx]=num[rx]+num[ry]+1;
}
}
int main(){
scanf("%d",&T);
for(int j=1;j<=30000;j++){
pre[j]=j;
}
memset(dis,0,sizeof(dis));
memset(num,0,sizeof(num));
while(T--){
scanf("%s",&s);
if(s[0]=='M'){
scanf("%d%d",&a,&b);
join(a,b);
}
else if(s[0]=='C'){
scanf("%d",&c);
printf("%d\n",num[find(c)]-dis[c]);
}
}
return 0;
}