洛谷P1196 [NOI2002]银河英雄传说
\(\Large\textbf{Description: } \large{30000个战舰,有两种操作,共k次(k \leq 500005):}\\\)
\(\quad\quad\large{M(i,j):把以i战舰为首的舰队接到j号战舰所在舰队尾部。\\}\)
\(\quad\quad\large{C(i,j):如果战舰i与战舰j在一个舰队,则输出他们间的战舰数;否则,输出-1。\\}\)
\(\Large\textbf{Solution: } \large{对于第一个操作,我们可以想到用并查集维护。\\对于第二个操作,考虑到询问次数很多,我们发现可以用前缀和做到O(1)查询,\\用top数组维护每个战舰与舰首之间的战舰数,那么ans = abs(top[i] - top[j]) - 1。\\考虑如何维护前缀和,因为每次移动都是整个舰队移动,所以舰队中每艘战舰与舰首之间的战舰数不变。如果i与j本来就在一个舰队,那么对我们的top没影响。\\如果一开始i与j并不在一个舰队里呢?对于一个i我们发现在find函数中可以顺便维护他的top数组,其余的战舰对结果没影响,我们不用处理。\\}\)
\(\Large\textbf{Code: }\)
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
#define gc() getchar()
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std;
const int N = 3e4 + 5;
int n, f[N], size[N], top[N];
inline int read() {
char ch = gc();
int ans = 0;
while (ch > '9' || ch < '0') ch = gc();
while (ch >= '0' && ch <= '9') ans = (ans << 1) + (ans << 3) + ch - '0', ch = gc();
return ans;
}
inline int find(int x) {
if (f[x] == x) return f[x];
int ff = find(f[x]);
top[x] += top[f[x]];
return f[x] = ff;
}
int main() {
n = read();
rep(i, 1, N) f[i] = i, size[i] = 1, top[i] = 0;
char s;
int x, y, no = -1;
while (n--) {
cin >> s;
x = read(), y = read();
int fx = find(x), fy = find(y);
if (s == 'M') {
top[fx] += size[fy];
size[fy] += size[fx];
f[fx] = fy;
size[fx] = 0;
}
else {
if (fx != fy) printf("-1\n");
else printf("%d\n", abs(top[x] - top[y]) - 1);
}
}
return 0;
}