加权并查集,简介见加权并查集,具体方法见代码及注释
//P1196 银河英雄传说 #include<cstdio> int fa[30010]; int r[30010];//r[i]表示第i号战舰在其父亲之后的第r[i]个位置 int r2[30010];//r2[i]表示以第i号战舰为队首的队列有r2[i]辆战舰 //本来只想到了记录战舰i后面的战舰数量,但是操作量太大,这里是受到其他人启发了 int t; int getans(int x,int y)//计算x与y之间战舰数量,"之间"指的就是不包括x和y本身 { if(x==y) return 0; if(x>y) return x-y-1; if(x<y) return y-x-1; } int find(int x) { if(fa[x]==x) return x; int t=find(fa[x]); r[x]+=r[fa[x]];//路径压缩,x在fa[x]之后的r[x]个位置,fa[x]在t之后的r[fa[x]]个位置,因此x在t之后的r[x]+r[fa[x]]个位置处 fa[x]=t; return t;//返回根结点 } void union1(int x,int y) { int fx=find(x); int fy=find(y); fa[fx]=fy;//将x所在集合(fx)并入y所在集合(fy) r[fx]=r2[fy];//fx集合所有战舰前方战舰数量都增加,但是暂时只更新根结点 r2[fy]+=r2[fx];//以fy为首的队列战舰数量增加fx集合的战舰数量 //r2[fx]=0;//fx集合战舰数量归零(没有这一步也没有关系,因为没有队列分离操作,这个值不再起作用) } int main() { int i,x,y,fx,fy; char c; scanf("%d",&t); for(i=1;i<=30000;i++) { fa[i]=i; r2[i]=1; } for(i=1;i<=t;i++) { scanf(" %c%d%d",&c,&x,&y); if(c=='M') union1(x,y); else { fx=find(x); fy=find(y); if(fx!=fy) printf("-1 "); else printf("%d ",getans(r[x],r[y])); } } return 0; }