专栏文章
题解:P8025 [ONTAK2015] Związek Harcerstwa Bajtockiego
P8025题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioctdb1
- 此快照首次捕获于
- 2025/12/02 17:06 3 个月前
- 此快照最后确认于
- 2025/12/02 17:06 3 个月前
挺好的一道树剖题。
首先我们思考我们会怎么走:
- 找到出发点和目标点的最近公共祖先(显然);
- 向“上”(节点深度减少)从出发点走到最近公共祖先;
- 向“下”(节点深度增加)走到目标点。
(记出发点为 ,目标点为 ,两点的最近公共祖先为 , 和 之间的最短路长度为 )。
我们可以分类讨论:
-
如果剩余步数 ,直接更新所在位置即可。
-
如果 ,那么先将 减去 ,再从 开始向下跳 步。
-
如果 ,那么从 开始向上跳 步。
也就是说我们每次处理询问先分讨,然后有两种操作:
- 向上跳;
- 向下跳。
用树剖可以轻松实现。
向上跳的操作:每次检查当前所在的重链是否可以被完全跳过,如果可以,就跳,并将 减去对应的步数。如果不能,直接在重链中寻找对应位置即可。
CPPint up(int x,int step){
while(step){//还有剩余步数
if(id[x]-id[top[x]]>=step){//如果不能跳过
x=tmp[id[x]-step];//这里做一下解释,树剖dfs2后会给每个节点一个标号,tmp[i]为标号为i的节点的最初序号
step=0;
break;
}
step-=id[x]-id[top[x]];//扣除步数
x=fa[top[x]];
step--;//注意从重链头跳到其父亲也算一步哦
}
return x;
}
向下跳不好直接实现,但我们换个思路:
和 由一条链连接,长为 ,从 往 跳 步等价于从 往 跳 步。
单次询问复杂度 ,可以通过此题。
代码:
CPP#include<bits/stdc++.h>
using namespace std;
int n,m,k;
struct node{
int v,w,next;
}edge[1000005<<1];
int a[1000005];
int head[1000005],num;
void add(int u,int v){
edge[++num].next=head[u];
head[u]=num;
edge[num].v=v;
}
int fa[1000005],dep[1000005],son[1000005],siz[1000005];
int id[1000005],tmp[1000005],top[1000005],cnt;
void dfs1(int now,int fath,int deep){
dep[now]=deep;
fa[now]=fath;
siz[now]=1;
int maxson=-1;
for(int i=head[now];i;i=edge[i].next){
int v=edge[i].v;
if(v==fath)continue;
dfs1(v,now,deep+1);
siz[now]+=siz[v];
if(maxson<siz[v]){
maxson=siz[v];
son[now]=v;
}
}
}
void dfs2(int now,int topf){
id[now]=++cnt;
tmp[cnt]=now;
top[now]=topf;
if(!son[now])return;
dfs2(son[now],topf);
for(int i=head[now];i;i=edge[i].next){
int v=edge[i].v;
if(v==fa[now]||v==son[now])continue;
dfs2(v,v);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
return x;
}
int up(int x,int step){
while(step){
if(id[x]-id[top[x]]>=step){
x=tmp[id[x]-step];
step=0;
break;
}
step-=id[x]-id[top[x]];
x=fa[top[x]];
step--;
}
return x;
}
int d,t;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs1(1,0,1);
dfs2(1,1);
while(k--){
scanf("%d%d",&d,&t);
int z=LCA(m,d);
if(dep[m]+dep[d]-dep[z]*2<=t){
m=d;
}
else if(dep[m]-dep[z]<=t){
t-=(dep[m]-dep[z]);
m=up(d,dep[d]-dep[z]-t);
}
else{
m=up(m,t);
}
printf("%d ",m);
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...