CF1006E Military Problem 题解

CF1006E Military Problem 题解

题意

给定一颗有 n(2n2×105) 个节点的树,树根为 1

对于每个节点 i(2in) 都有它的父节点 pi,并且每个节点的子节点都是按从小到大的顺序排列的的。

q(1q2×105) 个询问,每次询问都有一个 ui,ki(1ui,kin),输出以 ui 为根的子树的前序遍历中排名为 ki 的元素。

如果子树中没有 ki 个节点则输出 1

暴力做法

对于每个询问 iui,直接在以 ui 为根的子树中做前序遍历,取第 ki 个被遍历到的元素作为答案即可。

由于每次遍历都有可能遍历整棵树,所以时间复杂度最坏为 O(nq),会超时。

正解

继续在暴力做法上寻找优化方法。

如果我们画图会发现,一棵子树内前序遍历的编号是连续的。

所以我们就可以利用这一性质,在树的前序遍历上做询问。

先对整棵树跑一边前序遍历并将其记录下来。

在询问时,先找到 ui 在前序遍历中的下标 o,再找寻前序遍历中以下标 o 开始的第 ki 元素的下标 l

最终前序遍历中下标为 l 的元素即为所求答案。

对于输出 1 的情况,只需要记录一下以 i(1in) 为根的子树大小 sizei。在询问时,判断是否 sizeui<ki 即可。

由于预处理时间复杂度为 O(n),每次询问时间复杂度为 O(1),所以最终时间复杂度为 O(n+q),可以通过测试。

代码

#include<stdio.h>
#include<vector>
const int N=2e5+5;
std::vector<int> v[N];
int n,m,num[N],cnt,kth[N],size[N];
void build(int u){//预处理
	num[++cnt]=u;//前序遍历编号对应元素
	kth[u]=cnt;//元素对应编号
	size[u]=1;//子树大小
	for(auto son:v[u]) build(son),size[u]+=size[son];
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=2,x;i<=n;i++) scanf("%d",&x),v[x].emplace_back(i);
	build(1);
	int x,y;
	while(m--){
		scanf("%d%d",&x,&y);
		if(size[x]<y) puts("-1");
		else printf("%d\n",num[kth[x]+y-1]);
	}
	return 0;
}
posted @   gevenfeng  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示