题目链接如下:
是一道并查集的题目。不过也很明显要做一些改变。
思路1
利用父节点记录其子树有多少个节点,更新很简单,当把x所在stack堆叠到y所在的stack上时,
对于所有根节点是x的节点i,更新number[i] += number[y] + 1,这里number数组全部初始化成0代表初始没有子节点。
这样的缺点是每一次union操作都会引入NlogN+logN+logN,N在题目里边是10^4幂,P次操作可能直接爆掉。
思路2
反着来,记录当前节点到根节点的距离。
记录当前节点所在树的根节点的总节点数。
于是两者相减就是我们要的这个节点下边有多少个stack了。
实现:
1.节点到根节点的距离:
在寻根的过程中,先递归找到根节点,然后回溯过程中更新dis距离,更新方式是用该节点到原来根节点的距离加上原来根节点到现在根节点的距离。
2.根节点的总结点数
当把x所属的stack放到y所属的stack上时,我们知道,只要把原来x的根节点的节点总数数目加上y的stack的节点总数即可。
代码如下所示:
#include<cstdio>
#include<queue>
#include<stack>
#include<map>
#include<list>
#include<vector>
#include<string>
#include<iostream>
#include<ctime>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int MAXN = 30001;
int s[MAXN], ok[MAXN];
int son[MAXN],dis[MAXN];
int x,y;
int p;
void init_s(){
for (int i = 1; i <= MAXN; ++i) {
s[i]=i;
son[i]=1;
}
}
int find_s(int x){
if (x==s[x]) return x;
int tp=s[x];
s[x]= find_s(s[x]);
dis[x]+=dis[tp];
return s[x];
}
void union_s(int x,int y){
x = find_s(x);
y = find_s(y);
if (x!=y) {
s[y]=s[x];
dis[y]=son[x];
son[x]+=son[y];
}
}
int main() {
char op;
scanf("%d", &p);
getchar();
init_s();
while (p--){
scanf("%c", &op);
// cout<<"op:"<<op<<endl;
if (op=='M'){
scanf("%d %d", &x, &y);
// cout<<"x:"<<x<<",y:"<<y<<endl;
union_s(x,y);
} else if (op=='C'){
scanf("%d",&x);
printf("%d\n", son[find_s(x)]-dis[x]-1);
// cout<<"x:"<<x<<endl;
}
getchar();
}
return 0;
}