1. 程式人生 > >HihoCoder 1067 最近公共祖先·二

HihoCoder 1067 最近公共祖先·二

信息 dep 字符串 log i+1 ++ 緊急 next fat

最近公共祖先·二

時間限制:10000ms 單點時限:1000ms 內存限制:256MB

描述

上上回說到,小Hi和小Ho用非常拙劣——或者說粗糙的手段山寨出了一個神奇的網站,這個網站可以計算出某兩個人的所有共同祖先中輩分最低的一個是誰。遠在美國的他們利用了一些奇妙的技術獲得了國內許多人的相關信息,並且搭建了一個小小的網站來應付來自四面八方的請求。

但正如我們所能想象到的……這樣一個簡單的算法並不能支撐住非常大的訪問量,所以擺在小Hi和小Ho面前的無非兩種選擇:

其一是購買更為昂貴的服務器,通過提高計算機性能的方式來滿足需求——但小Hi和小Ho並沒有那麽多的錢;其二則是改進他們的算法,通過提高計算機性能的利用率來滿足需求——這個主意似乎聽起來更加靠譜。

於是為了他們第一個在線產品的順利運作,小Hi決定對小Ho進行緊急訓練——好好的修改一番他們的算法。

而為了更好的向小Ho講述這個問題,小Hi將這個問題抽象成了這個樣子:假設現小Ho現在知道了N對父子關系——父親和兒子的名字,並且這N對父子關系中涉及的所有人都擁有一個共同的祖先(這個祖先出現在這N對父子關系中),他需要對於小Hi的若幹次提問——每次提問為兩個人的名字(這兩個人的名字在之前的父子關系中出現過),告訴小Hi這兩個人的所有共同祖先中輩分最低的一個是誰?

輸入

每個測試點(輸入文件)有且僅有一組測試數據。

每組測試數據的第1行為一個整數N,意義如前文所述。

每組測試數據的第2~N+1行,每行分別描述一對父子關系,其中第i+1行為兩個由大小寫字母組成的字符串Father_i, Son_i,分別表示父親的名字和兒子的名字。

每組測試數據的第N+2行為一個整數M,表示小Hi總共詢問的次數。

每組測試數據的第N+3~N+M+2行,每行分別描述一個詢問,其中第N+i+2行為兩個由大小寫字母組成的字符串Name1_i, Name2_i,分別表示小Hi詢問中的兩個名字。

對於100%的數據,滿足N<=10^5,M<=10^5, 且數據中所有涉及的人物中不存在兩個名字相同的人(即姓名唯一的確定了一個人),所有詢問中出現過的名字均在之前所描述的N對父子關系中出現過,第一個出現的名字所確定的人是其他所有人的公共祖先。

輸出

對於每組測試數據,對於每個小Hi的詢問,按照在輸入中出現的順序,各輸出一行,表示查詢的結果:他們的所有共同祖先中輩分最低的一個人的名字。

樣例輸入
4
Adam Sam
Sam Joey
Sam Micheal
Adam Kevin
3
Sam Sam
Adam Sam
Micheal Kevin
樣例輸出
Sam
Adam
Adam

離線:Tarjan算法;

在線:ST算法;

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
const int maxn=200010;
int tot,ver[maxn],dept[maxn],first[maxn];
int cnt,Laxt[maxn],Next[maxn],To[maxn],vis[maxn];
int dp[maxn][20];
int personnum,m;//人數,問題數 
map<string,int>q;//q和name相對 
map<int,string>name;

void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt;
    To[cnt]=v;
}

void dfs(int u ,int dep)
{
    vis[u]=true;
    ver[++tot] = u;
    first[u] = tot;
    dept[tot] = dep;
    for(int i=Laxt[u];i;i=Next[i])
        if( !vis[To[i]] )
        {
            dfs(To[i],dep+1);
            ver[++tot] = u; 
            dept[tot] = dep;
        }
}

void ST(int n)
{
    for(int i=1;i<=n;i++)  dp[i][0] = i;
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
            dp[i][j] = dept[a]<dept[b]?a:b;
        }
    }
}

int RMQ(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=r-l+1)  k++;
    int a=dp[l][k],b=dp[r-(1<<k)+1][k];
    return dept[a]<dept[b]?a:b;
}

int LCA(int u ,int v)
{
    int x = first[u] , y = first[v];
    if(x > y) swap(x,y);
    int res = RMQ(x,y);
    return ver[res];
}
 
int init()
{
    int n,i;
    string s1,s2,Fa_all;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        
        cin>>s1>>s2;
        if(i==1)   Fa_all=s1;
        
        if(!q[s1]) q[s1]=++personnum;
        if(!q[s2]) q[s2]=++personnum;
        name[q[s1]]=s1;
        name[q[s2]]=s2;
        
        add(q[s1],q[s2]);
    }
    dfs(q[Fa_all],1); //搜索 
    ST(tot);//dp 
}

int main()
{
    init();
    string s1,s2;
    scanf("%d",&m);//問題 
    
    for(int i=1;i<=m;i++){
         cin>>s1>>s2;
         int u=q[s1];
         int v=q[s2];
         cout<<name[LCA(u,v)]<<endl;
    }
    return 0;
}

HihoCoder 1067 最近公共祖先·二