時間限制:
10000ms
單點時限:
1000ms
記憶體限制:
256mb 描述
上上回說到,小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
3sam sam
adam sam
micheal kevin
sam題解:題目用到了lac演算法,就是先把所有詢問讀取之後,在dfs中訪問到詢問的就判斷。adam
adam
如圖:dfs過程:先將1,2,3,4遍歷完,此時我從4開始,判斷詢問中是否有4,如果有,在判斷詢問中另外乙個是否訪問過,如果訪問過,最近祖先必然是另乙個的祖先(此時也就是他自身)。該點結束後,更新父節點,4->3,到了3,和4一樣,4->2,3->2;到了5,如果詢問中有5,並且另外乙個訪問過,最近祖先必然是另外乙個的祖先,以此類推。
總結:1.根節點的每乙個分支的共同祖先必然是根。例如:1的左分支和右分支的最近祖先必然是1;
2..dfs的目的就是從葉子開始查詢。然後更新父節點。每次找到乙個節點,就判斷是否出現在詢問中。
#include #include #include #include #include #include using namespace std;
mapmp;
vectorquery[100005]; //全部詢問
vectorv[100005]; //儲存孩子
string name[100005]; //儲存名字
int ans[100005]; //儲存每乙個詢問的結果
int fa[100005]; //父節點
string p1[100005]; //儲存這是第幾個詢問的前乙個名字
string p2[100005]; //儲存這是第幾個詢問的後乙個名字
int k = 0; //控制每乙個字串的編號
int solve(string& s) //匹配名字與數字
name[++k] = s;
return mp[s] = k;
}int find(int x) //並查集
return fa[x] = find(fa[x]);
}void dfs(int x,int pre) //dfs查詢結果
for(int i = 0;i < query[x].size();i++) //訪問該點是否詢問
ans[query[x][i]] = find(t); //找到了 第query[x][i]個詢問的答案 }
fa[x] = find(pre); //更新
}int main()
memset(fa,-1,sizeof(fa)); //表示沒有訪問過
cin>>m;
for(int i = 1;i <= m;i++)
//fa[0] = 0;
dfs(1,1); //寫成dfs(1,0),注意 fa[0]=-1,-1會成為下標
for(int i = 1;i <= m;i++)
{ cout<
最近公共祖先 python 最近公共祖先
lca演算法樸素演算法 也就是我們所說的暴力演算法,大致的思路是從樹根開始,往下迭代,如果當前結點比兩個結點都小,那麼說明要從樹的右子樹中找 相反則從左子樹中查詢 直到找到乙個結點在當前結點的左邊,乙個在右邊,說明當前結點為最近公共祖先,如果乙個結點是另外乙個結點的祖先,那麼返回前面結點的父親結點即...
最近公共祖先 LCA 最近公共祖先
直接暴力搜尋參考 普通搜尋每次查詢都需要 樸素演算法是一層一層往上找,倍增的話直接預處理出乙個 具體做法是 維護乙個 的關係來線性求出這個陣列 int anc n 31 int dep n 記錄節點深度 void dfs int u,int parent for int i 0 i g u size...
最近公共祖先 最近公共祖先(LCA)
如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。輸入格式 第一行包含三個正整數n m s,分別表示樹的結點個數 詢問的個數和樹根結點的序號。接下來n 1行每行包含兩個正整數x y,表示x結點和y結點之間有一條直接連線的邊 資料保證可以構成樹 接下來m行每行包含兩個正整數a b,表示詢問...