cogimyunの小窝

Loading...

Luogu P13019 [GESP202506 八级] 树上旅行 题解

首先,我们来分析一下题目中给出的两种移动方法:

  1. 移动至当前结点的父结点。特殊地,如果当前位于根结点,则不进行移动;
  2. 移动至当前结点的所有子结点中编号最小的结点。特殊地,如果当前位于叶子结点,则不进行移动。

预处理

不难发现,对于每个节点 \(i\) 来说,它最高能达到的节点一定是根节点 \(1\),最低能达到的节点同样是固定的。那么,我们不妨预处理一下 \(i\) 号节点的第 \(2^j\) 级祖先 \(f[i][j]\) 与其第 \(2^j\) 级子节点 \(son[i][j]\),当然 \(i\) 号节点的第 \(2^j\) 级子节点最多可能有 \(2^{j+1}\) 个,我们不可能维护 \(i\) 号节点的所有第 \(2^j\) 级子节点,所以我们只维护 \(i\) 号节点在第 \(2\) 种移动方式下会移动到的子节点,\(son[i][0]\) 维护的是 \(i\) 号节点的最小的一级子节点,\(son[i][1]\) 维护的是 \(son[i][0]\) 号节点的最小的一级子节点,\(son[i][2]\) 维护的是 \(son[i][1]\) 号节点的最小的二级子节点,以此类推

\[son[i][j]=son[son[i][j-1]][j-1] (i\geqslant2) \]

处理移动

接下来,让我们来处理移动,我们以向上移动 \(a_{i,j}\) 次为例,我们将当前位于节点 \(s\) 的深度 \(d[s]\)\(a_{i,j}\),特判 \(d[s]\) 是否等于 \(1\),如果等于 \(1\),我们就将 \(d[s]\)\(s\) 赋值为 \(1\),否则通过 \(f[s]\) 数组将 \(s\) 跳到这个深度。至于向下跳,我们只需注意将跳至 \(1\) 号根节点的特判改为跳至 \(son[s][18]\) 号最底层节点的特判,为什么 \(son[s][18]\) 就是最底层节点呢?因为我们可以将叶节点的 \(son[s][0]\) 记为 \(s\) 自己,这样由于 \(2^{18}=262144>100000=n\),那么在递归更新中 \(son[i][18]\) 一定会更新为 \(i\) 号节点的最底层节点。

最后的时间复杂度为 \(O(n+\log n(n+\displaystyle\sum^q_{i=1} k_i))\)

没想明白为什么不小心把向下跳更新 \(s\) 与向上跳更新 \(s\) 同时写成了向上跳更新 \(s\) 的函数后也是100pts(离谱)

code

#include<bits/stdc++.h>
using namespace std;
int n,q,f[100005][25],son[100005][25],d[100005],s,k;
priority_queue<int,vector<int>,greater<int> >e[100005];//通过优先队列维护小的子节点在前
void init(int x){//预处理
	for(int i=1;i<=18;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	bool f=true;
	int id;
	while(!e[x].empty()){
		if(f){
			son[x][0]=e[x].top();
			f=false;
		}
		d[e[x].top()]=d[x]+1;
		init(e[x].top());
		e[x].pop();
	}
	if(!son[x][0])
		son[x][0]=x;
	for(int i=1;i<=18;i++)
		son[x][i]=son[son[x][i-1]][i-1];
}
int down(int st,int de){
	for(int i=18;i>=0;i--){
		if(d[son[st][i]]<=de)
			st=son[st][i];
	}
	return st;
}
int up(int st,int de){
	for(int i=18;i>=0;i--){
		if(d[f[st][i]]>=de)
			st=f[st][i];
	}
	return st;
}
int main(){
	cin>>n>>q;
	for(int i=0;i<n-1;i++){
		int x;
		cin>>x;
		e[x].push(i+2);
		f[i+2][0]=x;
	}
	d[1]=1;
	init(1);
	for(int i=0;i<q;i++){
		cin>>s>>k;
		int x,p=d[s];
		for(int j=0;j<k;j++){
			cin>>x;
			if(x<0){//向下跳 
				p-=x;
				if(p>=d[son[s][18]])
					p=d[son[s][18]];
				s=down(s,p); 
			}
			if(x>0){//向上跳 
				p-=x;
				if(p<=1){
					s=1;
					p=1;
				}
				s=up(s,p);
			}
			
		}
		cout<<down(s,p)<<endl;
	}
	return 0;
}

posted @ 2025-10-30 18:28  cogimyun  阅读(1)  评论(0)    收藏  举报