AtCoder ABC239 E - Subtree K-th Max

发布时间 2023-04-07 21:23:12作者: 2huk

AtCoder ABC239 E - Subtree K-th Max

题目描述

给定一棵 \(n\) 个节点的树,\(i\) 节点的权值为 \(x_i\),根节点编号为 \(1\)

现有 \(Q\)个询问,每个询问给定 \(v,k\),求节点 \(v\) 的子树第 \(k\) 大的数。

输入输出样例

5 2
1 2 3 4 5
1 4
2 1
2 5
3 2
1 2
2 1
4
5

image

数据范围

\(0 \le x_i \le 10^9\)

\(2 \le n \le 10^5\)

\(1 \le Q \le 10^5\)

\(1 \le k \le 20\)

算法

DFS,树形结构

分析

如果对于每次询问都去访问子树,显然时间超限。

观察数据范围,会发现 \(1 \le k \le 20\),所以可以预处理出所有节点的子树的前 \(20\) 大。

处理大小,可以使用一个大顶堆来维护。

规定,\(ans_{i, j}\) 表示 \(i\) 点的子树中的第 \(j\) 大是多少。

那么,如何找到一棵子树的前 \(20\) 大呢?

image

如果要寻找 \(2\) 这个节点的前 \(20\) 大,那么就需要在它的儿子节点的子树中找到前 \(40\) 大,在从中找到前 \(20\) 大。因此对于每一个节点,首先需要遍历它的儿子节点,在这些节点中递归地寻找前 \(20\) 大的节点,并更新所有的 \(ans_{i, j}\)

DFS 过程结束后,对于每次询问,直接输出 \(ans_{v, k}\) 即可。

代码

#include <iostream>
#include <queue>

using namespace std;

#define int long long

const int N = 1e5 + 10;

// w[i] 表示第 i 个点的边权 
int n, q, w[N], a, b, v, k;
vector<int> g[N];
int ans[N][30];

priority_queue<int> heap, c; 

// 深度优先遍历 
void dfs(int u, int fa){
	ans[u][1] = w[u];
	
	for(auto v : g[u]){
		// 如果 v 点不是 u 的儿子节点,不做处理 
		if(v == fa){
			continue;
		}
		
		dfs(v, u);	// v 点的 ans 我们认为已经求解完毕 
		heap = c;	// 初始化 
		
		// 将原来已经存储好的 ans[u][i] 和现在的 ans[v][i] 进行合并,并找到前 20 大 
		for(int i=1; i<=20; i++){
			heap.push(ans[u][i]); 
			heap.push(ans[v][i]);
		}
		for(int i=1; i<=20; i++){
			ans[u][i] = heap.top();
			heap.pop();
		}
	}
}

signed main(){
	// 读入 
	scanf("%lld %lld", &n, &q);
	
	for(int i=1; i<=n; i++){
		scanf("%lld", w+i); 
	}
	
	for(int i=1; i<n; i++){
		scanf("%lld %lld", &a, &b);
		// 建树 
		g[a].push_back(b);
		g[b].push_back(a);
	}
	
	// 遍历 
	dfs(1, 0);
	
	// 访问并输出 
	while(q--){
		scanf("%lld %lld", &v, &k);
		printf("%lld\n", ans[v][k]);
	}
	
	return 0;
}