1. 程式人生 > >P1196 [NOI2002]銀河英雄傳說

P1196 [NOI2002]銀河英雄傳說

銀河系 根節點 點名 -o alpha 節點 自己的 con load

題目描述

公元五八○一年,地球居民遷至金牛座α第二行星,在那裏發表銀河聯邦創立宣言,同年改元為宇宙歷元年,並開始向銀河系深處拓展。

宇宙歷七九九年,銀河系的兩大軍事集團在巴米利恩星域爆發戰爭。泰山壓頂集團派宇宙艦隊司令萊因哈特率領十萬余艘戰艦出征,氣吞山河集團點名將楊威利組織麾下三萬艘戰艦迎敵。

楊威利擅長排兵布陣,巧妙運用各種戰術屢次以少勝多,難免恣生驕氣。在這次決戰中,他將巴米利恩星域戰場劃分成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行,每行有一條指令。指令有兩種格式:

  1. M i j :i和j是兩個整數(1<=i , j<=30000),表示指令涉及的戰艦編號。該指令是萊因哈特竊聽到的楊威利發布的艦隊調動指令,並且保證第i號戰艦與第j號戰艦不在同一列。

  2. 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]銀河英雄傳說