P1196 [NOI2002]銀河英雄傳說
題目描述
公元五八○一年,地球居民遷至金牛座α第二行星,在那裏發表銀河聯邦創立宣言,同年改元為宇宙歷元年,並開始向銀河系深處拓展。
宇宙歷七九九年,銀河系的兩大軍事集團在巴米利恩星域爆發戰爭。泰山壓頂集團派宇宙艦隊司令萊因哈特率領十萬余艘戰艦出征,氣吞山河集團點名將楊威利組織麾下三萬艘戰艦迎敵。
楊威利擅長排兵布陣,巧妙運用各種戰術屢次以少勝多,難免恣生驕氣。在這次決戰中,他將巴米利恩星域戰場劃分成30000列,每列依次編號為1, 2, …,30000。之後,他把自己的戰艦也依次編號為1, 2, …, 30000,讓第i號戰艦處於第i列(i = 1, 2, …, 30000),形成“一字長蛇陣”,誘敵深入。這是初始陣形。當進犯之敵到達時,楊威利會多次發布合並指令,將大部分戰艦集中在某幾列上,實施密集攻擊。合並指令為M i j,含義為第i號戰艦所在的整個戰艦隊列,作為一個整體(頭在前尾在後)接至第j號戰艦所在的戰艦隊列的尾部。顯然戰艦隊列是由處於同一列的一個或多個戰艦組成的。合並指令的執行結果會使隊列增
大。 然而,老謀深算的萊因哈特早已在戰略上取得了主動。在交戰中,他可以通過龐大的情報網絡隨時監聽楊威利的艦隊調動指令。
在楊威利發布指令調動艦隊的同時,萊因哈特為了及時了解當前楊威利的戰艦分布情況,也會發出一些詢問指令:C i j。該指令意思是,詢問電腦,楊威利的第i號戰艦與第j號戰艦當前是否在同一列中,如果在同一列中,那麽它們之間布置有多少戰艦。
作為一個資深的高級程序設計員,你被要求編寫程序分析楊威利的指令,以及回答萊因哈特的詢問。
最終的決戰已經展開,銀河的歷史又翻過了一頁……
輸入輸出格式
輸入格式:輸入文件galaxy.in的第一行有一個整數T(1<=T<=500,000),表示總共有T條指令。
以下有T行,每行有一條指令。指令有兩種格式:
-
M i j :i和j是兩個整數(1<=i , j<=30000),表示指令涉及的戰艦編號。該指令是萊因哈特竊聽到的楊威利發布的艦隊調動指令,並且保證第i號戰艦與第j號戰艦不在同一列。
-
C i j :i和j是兩個整數(1<=i , j<=30000),表示指令涉及的戰艦編號。該指令是萊因哈特發布的詢問指令。
輸出文件為galaxy.out。你的程序應當依次對輸入的每一條指令進行分析和處理:
如果是楊威利發布的艦隊調動指令,則表示艦隊排列發生了變化,你的程序要註意到這一點,但是不要輸出任何信息;
如果是萊因哈特發布的詢問指令,你的程序要輸出一行,僅包含一個整數,表示在同一列上,第i 號戰艦與第j 號戰艦之間布置的戰艦數目。如果第i 號戰艦與第j號戰艦當前不在同一列上,則輸出-1。
輸入輸出樣例
輸入樣例#1:4
M 2 3
C 1 2
M 2 4
C 4 2
輸出樣例#1:
-1
1
說明
【樣例說明】
戰艦位置圖:表格中阿拉伯數字表示戰艦編號
Solution:
學習了一下“擴展域”與“邊帶權”的並查集,本題就是一道典型的帶權並查集。
並查集實際上是一個包含若幹樹的森林,我們維護$d$數組,其中$d[x]$表示節點$x$到父節點$fa[x]$之間的邊權。我們在路徑壓速時,同時對$d[x]$進行更新,就能做到維護節點到根的信息。
本題實現查找時,在普通並查集的$find(x)$(查找$x$父節點的函數)裏,路壓後加上$d[x]+=d[fa[x]]$就能維護節點到根的距離。
合並時,維護數組$siz$($siz[x]$表示$x$以為根的子樹節點數,初始值為$1$),在普通並查集的$merge(x,y)$(合並$x$和$y$節點的函數)裏,將$fa[x]$指向$fa[y]$後,使得$d[x]=siz[y]$(因為$x$加在$y$的末尾,所以$x$到$y$的距離為$y$子樹節點數),並更新以$y$為根的子樹節點數$siz[y]+=siz[x]$。
那麽本題的查詢兩節點$x,y$的距離,就是$|d[x]-d[y]|-1$(即$x,y$到根節點的距離差再減$1$),其它操作和普通並查集一樣的。
代碼:
1 // luogu-judger-enable-o2 2 #include<bits/stdc++.h> 3 #define il inline 4 #define ll long long 5 using namespace std; 6 const int N=30005; 7 int fa[N],d[N],siz[N],n; 8 il int gi(){ 9 int a=0;char x=getchar(); 10 while(x<‘0‘||x>‘9‘)x=getchar(); 11 while(x>=‘0‘&&x<=‘9‘)a=a*10+x-48,x=getchar(); 12 return a; 13 } 14 il int find(int x){ 15 if(x==fa[x])return fa[x]; 16 int father=find(fa[x]); 17 d[x]+=d[fa[x]]; 18 return fa[x]=father; 19 } 20 il void merge(int x,int y){ 21 x=find(x),y=find(y); 22 fa[x]=y,d[x]=siz[y]; 23 siz[y]+=siz[x]; 24 } 25 int main() 26 { 27 n=gi(); 28 for(int i=1;i<=30000;i++)fa[i]=i,siz[i]=1; 29 char s[2];int u,v; 30 while(n--){ 31 scanf("%s",s); 32 u=gi(),v=gi(); 33 if(s[0]==‘M‘)merge(u,v); 34 else { 35 if(find(u)==find(v))printf("%d\n",abs(d[u]-d[v])-1); 36 else printf("-1\n"); 37 } 38 } 39 return 0; 40 }
P1196 [NOI2002]銀河英雄傳說