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

洛谷P1196 銀河英雄傳說

一點 esp 計算 註意 輸入 絕對值 sta 戰略 mes

技術分享

題目描述

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

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

威利組織麾下三萬艘戰艦迎敵。楊威利擅長排兵布陣,巧妙運用各種戰術屢次以少勝多,難免恣生驕氣。在這次決戰中,他將巴米利恩星域戰場劃分成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號戰艦不在同一列。

  1. C i j :i和j是兩個整數(1<=i , j<=30000),表示指令涉及的戰艦編號。該指令是萊因哈特發布的詢問指令。

輸出格式:

輸出文件為galaxy.out。你的程序應當依次對輸入的每一條指令進行分析和處理:

如果是楊威利發布的艦隊調動指令,則表示艦隊排列發生了變化,你的程序要註意到這一點,但是不要輸出任何信息;

如果是萊因哈特發布的詢問指令,你的程序要輸出一行,僅包含一個整數,表示在同一列上,第i 號戰艦與第j 號戰艦之間布置的戰艦數目。如果第i 號戰

艦與第j號戰艦當前不在同一列上,則輸出-1。

*****************************************************************************

並查集判斷是否在一列中是很容易想到的,但是比較麻煩的是怎樣計算他們中間的戰艦的個數

試想一下,在一列中,我們知道了要找的兩個數分別排在第幾,就很容易可以得到中間的相隔,比如說 扶桑(ふそう)在第三個,大和(やまと)在

第五個,他們中間就間隔了1個 有木有                           (廢話有木有)

因此,一個記錄深度的數組,就是非常必要的啦。同時,為了輔助他,我們還要一個記錄隊列長度的數組。

具體操作:

將一列戰艦放到另一列戰艦之後

方法::加入a【有三個艦】,b【有五個艦】,將a放到b之後,那麽a【第一個艦】的深度當然就是b的長度+1=6了,然後新列長度就是原來長度之和

處理列中每一個艦的深度

方法::遞歸,具體怎麽做看代碼

求出兩艦之間間隔

方法::先判斷是否在一列,然後取deep之差的絕對值再減去1即可

************************************

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 30001
using namespace std;
int father[maxn];
int deep[maxn];/*記錄每個元素在當前隊列中的深度*/
int len[maxn];/*記錄當前隊列長度*/
int read()
{
    int num = 0;
    char c = getchar();
    int f = 1;
    while(c < 0||c > 9)
    {
        if(c == -)f = -1;
        c = getchar();
    }
    while(c >= 0 && c <= 9)
    {
        num *= 10;
        num += c - 0;
        c = getchar();
    }
    return num * f;
}
int find(int x)
{
    if(father[x] == x)
      return x;
    else
    {
        int note = father[x];/*這裏有點繞,先記錄下當前的父節點給下面用,下面的更改deep是在回溯的時候進行的*/
        father[x] = find(father[x]); /*不然會導致算後面深度時前面的沒算好*/
        deep[x] = deep[x] + deep[note] - 1;/*加上一句求深度*/
        return father[x];
    }
}
void unionn(int vi,int vj)
{
    int h = find(vi);int f = find(vj);
    father[h] = f;
    deep[h] = len[f] + 1;/**整個連接到後面,後面串的開頭深度即為上一串長度+1**/
    len[f] += len[h]; /*兩串合並為一串深度相加*/ 
}
int ask(int a,int b)
{
    return abs(deep[a] - deep[b]) - 1;/*中間有多少即為兩者深度相減懶猴減去一個端點*/
}                                                    /*因為兩個端點都不包含*/
int main()
{
    for(int i = 1;i <= 30000;i ++)
    {
        father[i] = i;
        deep[i] = 1;
        len[i] = 1;
    }
    int T = read();
    while(T--)
    {
        char c;
        cin>>c;
        int a = read();
        int b = read();
        switch(c)
        {
            case M:
                unionn(a,b);
                break;
            case C:
                if(find(a) == find(b))
                  printf("%d\n",ask(a,b));
                else
                  puts("-1");
                break;
        }
    }    
    return 0;
} 

洛谷P1196 銀河英雄傳說