一年没写博客了(滑稽)。
这道题很玄学,导致自己都有一个坑人的问题求解。如果有大佬有能力求帮助:https://www.luogu.org/discuss/show?postid=30231
再来讲一下我对这道题的理解吧。
首先,对于判断两个数是否在同一列战舰中,我们只需要朴素的并查集就可以。
但这道题还要求我们去搞一个两船之间的距离(这就很难办了),所以只能使用带权并查集。
我们开一个数组front[],来表示第i号战舰到i所在的战舰的队头的距离。这样如果两个战舰在同一列中,他们的距离就是abs(front[i]-front[j])-1(因为这两辆都不包括,因此-1)。
这样是很方便,但当我们合并时,就会发生front[]值失去对应性。所以我们要不断地更新front[]。
再开一个数组len[]表示以i为队头的队列的长度,这样在把i接到j后面时,只需要让front[fi]+=len[fj],并把len[fj]清零即可。
最后对于front[]的更新,只需要在getfather时回溯完成即可。
然后你就学会了如何打带权并查集(欧耶)!
CODE
#include<cstdio> using namespace std; const int N=30005; int father[N],front[N],len[N],i,t,x,y; inline void read(int &x) { x=0; char ch=getchar(); while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); } inline void write(int x) { if (x<0) putchar('-'),x=-x; if (x/10) write(x/10); putchar(x%10+'0'); } inline int getfather(int k) { if (father[k]==k) return k; int fa=getfather(father[k]); front[k]+=front[father[k]]; return father[k]=fa; } inline int abs(int a) { return a<0?-a:a; } int main() { //freopen("testdata.in","r",stdin); freopen("my_out.out","w",stdout); for (i=1;i<N;++i) father[i]=i,len[i]=1; read(t); while (t--) { char ch=getchar(); while (ch!='M'&&ch!='C') ch=getchar(); read(x); read(y); int fx=getfather(x),fy=getfather(y); if (ch=='M') { front[fx]+=len[fy]; father[fx]=fy; len[fy]+=len[fx]; len[fx]=0; } else write(fx==fy?abs(front[x]-front[y])-1:-1),putchar(' '); } return 0; }