P1196 [NOI2002]银河英雄传说[带权并查集]
对于任意两艘舰,查询是否在同一列就是看在不在同一集合,并查集。询问距离的话,把每个点和他父亲的连边带上权值表示距离(父亲不一定就是前一艘舰),查询时会顺便压缩路径,一路回来会把所有点直接连边向根节点,这时边权改为他父亲(已直指根节点)的dis加上自己到父亲的dis,再连边向根。所以每次询问会把两个点到根的距离处理好,就可以出结果了。对于路径已压缩的点再查也不会出错。
然后是合并问题。将x的根要并到y集合中,而x的根迟早是要被压缩路径的,不妨现在就把他连一个边到y的根节点权值是y集合战舰数量(也就是把x根接在y最后,其到队首的距离)。
上述均摊复杂度$O(NlogN)$,然后可以加一些合并的优化什么的,复杂度基本就是$O(Nα(N))$了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #define dbg(x) cerr<<#x<<" = "<<x<<endl 8 #define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl 9 using namespace std; 10 typedef long long ll; 11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 13 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 14 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 15 template<typename T>inline T _abs(T A){return A<0?-A:A;} 16 template<typename T>inline T read(T&x){ 17 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 18 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 19 } 20 const int N=30000+7; 21 int fa[N],dis[N],siz[N],T,x,y; 22 char s[3]; 23 inline int Get(int x){ 24 if(fa[x]==x)return x; 25 int ret=Get(fa[x]); 26 dis[x]+=dis[fa[x]]; 27 return fa[x]=ret; 28 } 29 inline void Merge(int x,int y){ 30 int fx=Get(x),fy=Get(y); 31 if(siz[fx]>siz[fy])x^=y^=x^=y;//按秩合并的优化 32 fa[fx]=fy,dis[fx]=siz[fy],siz[fy]+=siz[fx]; 33 } 34 35 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout); 36 read(T); 37 for(register int i=1;i<=3e4;++i)fa[i]=i,siz[i]=1;//dis[i]=0; 38 while(T--){ 39 scanf("%s",s);read(x),read(y); 40 if(s[0]=='M')Merge(x,y); 41 else Get(x)^Get(y)?printf("-1\n"):printf("%d\n",_abs(dis[x]-dis[y])-1); 42 } 43 return 0; 44 }