描述
\(link\)
有标号为 \(1\) 到 \(n\) 的 \(n\) 个龙珠,分别放在对应标号为 \(1\) 到 \(n\) 的 \(n\) 个城市里。
下面有两种操作:T A B
表示把 A 龙珠所在城市的所有龙珠都转移到 B 龙珠所在的城市中Q A
表示查询 A ,需要知道 A 龙珠现在所在的城市,A 所在的城市有几颗龙珠,A 转移到这个城市移动了多少次,分别输出 \(3\) 个整数,表示上述信息。
题解
所在城市和移动次数在这里就不在赘述(学过并查集都会)
本题相对于普通的并查集多了一个转移次数这个东西,我们可以用懒惰标记(详见OI-WIKI - 线段树)的思想来记录。具体操作如下:
用 \(vis\) 来表示转移次数。显然,每个集合的代表转移次数为 \(0\)。执行T操作时,A集合的代表肯定的转移次数会变 \(1\),更新成员的转移次数时,只用加上其父亲的 \(vis\) 值即可。
\(code\)
#include <bits/stdc++.h>
using namespace std;
int n, m;
char c;
int father[10005];
int cnt[10005];
int vis[10005];
int a, b;
void init() {
for (int i = 1; i <= n; i++) father[i] = i, cnt[i] = 1, vis[i] = 0;
}
int find(int x) {
if (x == father[x]) {
return x;
} else {
int t = find(father[x]);
vis[x] += vis[father[x]];
return father[x] = t;
}
}
inline void read(int &a) {
a = 0;
bool f = false;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = true;
c = getchar();
}
while (c >= '0' && c <= '9') {
a = (a << 1) + (a << 3) + c - '0';
c = getchar();
}
if (f) a *= -1;
}
inline void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x < 10) {
putchar(x + '0');
return;
}
write(x / 10);
putchar(x % 10 + '0');
}
int main() {
int t;
read(t);
for (int i = 1; i <= t; i++) {
read(n), read(m);
init();
printf("Case %d:\n", i);
for (int i = 1; i <= m; i++) {
c = getchar();
if (c == 'T') {
read(a), read(b);
int x = find(a), y = find(b);
if (x == y) continue;
father[x] = y;
cnt[y] += cnt[x];
cnt[x] = 0;
vis[x] = 1;
} else if (c == 'Q') {
read(a);
write(find(a));
putchar(' ');
write(cnt[find(a)]);
putchar(' ');
find(a);
write(vis[a]);
putchar('\n');
}
}
}
return 0;
}