NOI2002 银河英雄传说
这道题很明显是一道并查集 + 维护并查集中元素到根结点之间距离的题。
具体的维护方法也很简单。fa数组表示每个点的父亲,dis数组表示当前节点到父亲的距离,sum数组表示当前节点所在集合中元素个数。预处理的时候把每个点的dis设为0,sum设为1.
之后对于每次战舰合并操作,将其中一个战舰所在集合合并至另一个战舰所在集合,被合并的集合的dis要加上另一个的sum,另一个集合的sum加上被合并集合的sum,并把被合并的集合的sum清零。询问操作就是先求出两艘战舰之间dis之差的绝对值,之后再-1.
然后对于找父亲的操作,我们比普通的并查集多了一个维护距离,只要先更新自己的父亲的dis,之后对于自己,则有dis[x] += dis[fa[x]]即可。
这个还是很好理解的……上代码。(好像这题用我的快读有bug……?没办法用了cin)
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<iostream> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; const int M = 30005; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } char c; int t,fa[M+5],dis[M+5],num[M+5],a,b; int getfa(int x) { if(fa[x] == x) return fa[x]; int fx = getfa(fa[x]); dis[x] += dis[fa[x]]; return fa[x] = fx; } int main() { // freopen("a.in","r",stdin); rep(i,1,M) fa[i] = i,num[i] = 1; cin >> t; while(t--) { cin >> c >> a >> b; int r1 = getfa(a),r2 = getfa(b); if(c == 'M') { dis[r1] += num[r2],fa[r1] = r2; num[r2] += num[r1],num[r1] = 0; } if(c == 'C') { if(r1 != r2) printf("-1\n"); else printf("%d\n",abs(dis[a] - dis[b]) - 1); } } return 0; }
当你意识到,每个上一秒都成为永恒。