[ NOI 2002 ] 銀河英雄傳說
阿新 • • 發佈:2018-11-06
\(\\\)
Description
有 \(n\) 列戰場,每一列一開始只有一個戰艦,編號就是對應的戰場編號。
有 \(m\) 次操作:
- \(M_{i,j}\) :把 \(i\) 所在的一整列接在 \(j\) 所在的一列的最後面。
- \(C_{i,j}\) :查詢 \(i,j\) 是否在同一列裡,若在的話輸出兩者之間隔了多少個戰艦。
注意每一列戰場的戰艦都是排成一列的。
\(\\\)
Solution
帶偏移量的並查集。
記錄 \(d[x]\) 表示 \(x\) 到所在列頭的一段上共有多少個戰艦。
記錄 \(sz[x]\) 表示 \(x\) 所在的一列有多少個戰艦。
為了保證複雜度,我們在做的時候還是要路徑壓縮。
但是 \(d[x]\) 怎麼保證正確?遞迴的時候這麼寫就好了。
int find(int x){
if(x==f[x]) return x;
int fa=find(f[x]);
d[x]+=d[f[x]];
return f[x]=fa;
}
注意是 \(d[x]+=d[f[x]]\),因為之前可能路徑壓縮過。還有注意 $f[x] $ 的更新時間。
合併的時候,記 \(f_i\) 表示 \(i\) 所在一列的列頭,\(f_j\) 表示 \(j\) 所在的列頭,有
\[ d[f_i]=sz[f_j] \]
查詢的時候,\(find\) 的過程中已經保證了兩位置的 \(d\) 陣列數值正確,所以在一列的兩個戰艦答案就是
\[ |d[x]-d[y]|-1 \]
\(\\\)
Code
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 30010 #define R register #define gc getchar using namespace std; inline int rd(){ int x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } struct UFS{ int f[N],d[N],sz[N]; UFS(){for(R int i=1;i<N;++i){f[i]=i;sz[i]=1;}} int find(int x){ if(x==f[x]) return x; int fa=find(f[x]); d[x]+=d[f[x]]; return f[x]=fa; } inline void merge(int x,int y){ int fx=find(x),fy=find(y); d[fx]=sz[fy]; f[fx]=fy; sz[fy]+=sz[fx]; } }ufs; int main(){ char op; int t=rd(),x,y; while(t--){ op=gc(); while(!isalpha(op)) op=gc(); if(op=='M'){x=rd();y=rd();ufs.merge(x,y);} else{ x=rd(); y=rd(); if(ufs.find(x)!=ufs.find(y)) puts("-1"); else printf("%d\n",abs(ufs.d[x]-ufs.d[y])-1); } } return 0; }