题目链接如下:
这里介绍三种处理转移次数的方法,都是并查集的思路。
进行T x y的时候,即把x所在地的全部搬到y所在地。如何更新x里边的龙珠的转移次数呢?
思路一:
在把x集合合并到y的集合之前,将节点的根是rootx的节点的转移次数加一,这样每次都会有nlogn的开销,然后总的开销最坏是qnlogn,直接爆炸。
思路二:
不进行路径压缩,直接向上查找根节点,记录向上查找的次数。这个次数其实就是移动的次数了。
这个时间复杂度大概最坏是qn,刚刚好的样子。也确实能过。
思路三:
进行路径压缩,当遇到T x y的操作时,只将x的根节点的次数加一,并且进行路径压缩。这样的好处是,在find的同时也更新路径上节点的转移次数,这里所有之前的节点存储的是累计转移次数,时间复杂度是qlogn。
代码如下所示:
#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 = 10001;
struct node{
int f;
int times;
int num;
}s[MAXN];
int x,y;
int n,q,t;
char op;
void init_s(){
for (int i = 1; i <= n; ++i) {
s[i].f=i;
s[i].times=0;
s[i].num=1;
}
}
int find_s(int x){
if (s[x].f==x) return x;
int tmp=s[x].f;
s[x].f=find_s(s[x].f);
s[x].times+=s[tmp].times;
return s[x].f;
}
void union_s(int x,int y){
int rootx= find_s(x);
int rooty= find_s(y);
if (rootx!=rooty){
s[rootx].f=rooty;
s[rooty].num+=s[rootx].num;
s[rootx].times++;
}
}
int main() {
scanf("%d", &t);
for (int k = 1; k <= t; ++k) {
scanf("%d %d", &n, &q);
init_s();
printf("Case %d:\n", k);
for (int i = 0; i < q; ++i) {
getchar();
scanf("%c", &op);
if (op == 'T') {
scanf("%d %d", &x, &y);
union_s(x, y);
} else {
scanf("%d", &x);
int root = find_s(x);
printf("%d %d %d\n", root, s[root].num, s[x].times);
}
}
}
return 0;
}