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

洛谷P1196 [NOI2002]銀河英雄傳說

題目描述:

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

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

楊威利擅長排兵佈陣,巧妙運用各種戰術屢次以少勝多,難免恣生驕氣。在這次決戰中,他將巴米利恩星域戰場劃分成3000030000列,每列依次編號為1, 2, …,300001,2,,30000。之後,他把自己的戰艦也依次編號為1, 2, …, 300001,2,,30000,讓第ii號戰艦處於第ii列(i = 1, 2, …, 30000)(i=1,2,,30000),形成“一字長蛇陣”,誘敵深入。這是初始陣形。當進犯之敵到達時,楊威利會多次釋出合併指令,將大部分戰艦集中在某幾列上,實施密集攻擊。合併指令為M_{i,j}Mi,j,含義為第i號戰艦所在的整個戰艦佇列,作為一個整體(頭在前尾在後)接至第j號戰艦所在的戰艦佇列的尾部。顯然戰艦佇列是由處於同一列的一個或多個戰艦組成的。合併指令的執行結果會使佇列增大。

然而,老謀深算的萊因哈特早已在戰略上取得了主動。在交戰中,他可以通過龐大的情報網路隨時監聽楊威利的艦隊調動指令。

在楊威利釋出指令調動艦隊的同時,萊因哈特為了及時瞭解當前楊威利的戰艦分佈情況,也會發出一些詢問指令:C_{i,j}Ci,j。該指令意思是,詢問電腦,楊威利的第ii號戰艦與第jj號戰艦當前是否在同一列中,如果在同一列中,那麼它們之間佈置有多少戰艦。

作為一個資深的高階程式設計員,你被要求編寫程式分析楊威利的指令,以及回答萊因哈特的詢問。

最終的決戰已經展開,銀河的歷史又翻過了一頁……

 

題解:

並查集裸題。

我們用到帶權並查集。

對於每個點,分別記錄所屬鏈的頭結點、該點到頭結點的距離以及它所在集合的大小。

每次合併將y接在x的尾部,改變y頭的權值和所屬鏈的頭結點,同時改變x的尾節點。

注意:每次查詢的時候也要維護每個節點的權值。

每次查詢時計算兩點的權值差。

附上程式碼:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int n,a,b,z,c;
char k[10];
struct Edge
{
    int front,fa,tail;
}f[30001];
int find(int
p) { if(f[p].fa==p) return p; int t=find(f[p].fa); f[p].front+=f[f[p].fa].front; return f[p].fa=t; } void merge(int x,int y) { int fx=find(x); int fy=find(y); if(fx!=fy) { f[fx].fa=fy; f[fx].front+=(f[fy].tail+1); f[fy].tail+=(f[fx].tail+1); } } int main() { scanf("%d",&n); for(int i=1;i<=30000;i++) { f[i].fa=i; f[i].front=f[i].tail=0; } for(int i=1;i<=n;i++) { scanf("%s%d%d",k,&a,&b); if(k[0]=='M') merge(a,b); else { z=find(a); c=find(b); if(z==c) printf("%d\n",max(f[a].front,f[b].front)-min(f[a].front,f[b].front)-1); else printf("-1\n"); } } }