1. 程式人生 > 其它 >acwing 4310. 樹的DFS

acwing 4310. 樹的DFS

目錄

題目傳送門

題目描述

給定一棵 nn 個節點的樹。

節點的編號為 1∼n1∼n,其中 11 號節點為根節點,每個節點的編號都大於其父節點的編號。

現在,你需要回答 qq 個詢問。

每個詢問給定兩個整數 ui,kiui,ki。

我們希望你用 DFS(深度優先搜尋)演算法來遍歷根節點為 uiui 的子樹。

我們規定,當遍歷(或回溯)到某一節點時,下一個遍歷的目標應該是它的未經遍歷的子節點中編號最小的那一個子節點。

例如,上圖例項中:

  • 如果遍歷根節點為 11 號節點的子樹,則子樹內各節點的遍歷順序為 [1,2,3,5,6,8,7,9,4][1,2,3,5,6,8,7,9,4]。
  • 如果遍歷根節點為 33 號節點的子樹,則子樹內各節點的遍歷順序為 [3,5,6,8,7,9][3,5,6,8,7,9]。
  • 如果遍歷根節點為 77 號節點的子樹,則子樹內各節點的遍歷順序為 [7,9][7,9]。
  • 如果遍歷根節點為 99 號節點的子樹,則子樹內各節點的遍歷順序為 [9][9]。

每個詢問就是讓你計算採用規定的 DFS 演算法來遍歷根節點為 uiui 的子樹時,第 kiki 個被遍歷到的節點的編號。

輸入格式

第一行包含兩個整數 n,qn,q。

第二行包含 n−1n−1 個整數 p2,p3,…,pnp2,p3,…,pn,其中 pipi 表示第 ii 號節點的父節點的編號。

接下來 qq 行,每行包含兩個整數 ui,kiui,ki,表示一組詢問。

輸出格式

共 qq 行,每組詢問輸出一行一個整數表示第 kiki 個被遍歷到的節點的編號。

如果第 kiki 個被遍歷到的節點不存在,則輸出 −1−1。

資料範圍

前三個測試點滿足 2≤n≤202≤n≤20,1≤q≤201≤q≤20。
所有測試點滿足 2≤n≤2×1052≤n≤2×105,1≤q≤2×1051≤q≤2×105,1≤pi<i1≤pi<i,1≤ui,ki≤n1≤ui,ki≤n。

輸入樣例:

9 6
1 1 1 3 5 3 5 7
3 1
1 5
3 4
7 3
1 8
1 9

輸出樣例:

3
6
8
-1
9
4

dfs演算法求解

分析

首先使用dfs,用path陣列記錄整個dfs的遍歷順序,(注意對於節點u的每個節點,要按編號從小到大遍歷)

遍歷過程中記錄每個以u為根的子樹的大小cnt[u](包括u本身)

然後用一個數組order記錄某個節點u在path中的下標


對於每個詢問,u,k

  • 首先看k的大小是否超過了以u為根的子樹大小
    • 超過輸出-1
    • 不超過的話,首先用order[u]找到upath中的下標,然後往後數k-1個就是目標節點

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<unordered_map>
#include<cmath>
using namespace std;
const int N = 200010;
vector<int> h[N];
vector<int> path;
int n, q;

int order[N];
int cnt[N]; // cnt[i]表示以i為根的子樹的大小 

int k;

int dfs(int u)
{
	path.push_back(u);
	
	cnt[u] = 1; // u本身是該子樹的一個節點
	for(int i = 0; i < h[u].size(); i++)
	{
		int j = h[u][i];
		cnt[u] += dfs(j);
	}
	return cnt[u];
}
int main()
{
	scanf("%d%d", &n, &q);
	for(int i = 2; i <= n; i++) 
	{
	    int a;
		scanf("%d", &a);
		h[a].push_back(i);
	}
	for(int i = 1; i <= n; i++) sort(h[i].begin(), h[i].end());
	
 	path.push_back(0);
	dfs(1);
	
//	for(int i = 1; i < path.size(); i++) cout << path[i] << " ";
//	return 0;
	
	for(int i = 1; i < path.size(); i++)  order[path[i]] = i; // 記錄path[i]在遍歷順序中的下標
	
	while(q --)
	{
	    int u, k;
	    scanf("%d%d", &u, &k);
	    if(k <= cnt[u]) printf("%d\n",path[order[u] + k - 1]); // order[u]表示u在path[]中的下標
	    else            printf("-1\n");
	}
	return 0;
}

時間複雜度

\(O(n)\)

參考文章

https://www.acwing.com/file_system/file/content/whole/index/content/4147870/