题解 【[JRKSJ R6] INFiNiTE ENERZY -Overdoze-】
题意
给你一颗 个结点的树,有 次询问,每次给出 ,求任意一个与 距离为 的点,或者返回无解。
。
思路
先定 为根,求出每个结点深度 ,子树里深度的最大值 和取到最大值的结点 。
对于一次询问分一下类:
- ,直接输出 级祖先即可。
- ,此时要输出一个 结点的 级儿子。我们可以知道,最深的链上一定有这种结点,于是转化为 的 级祖先。
- 其他情况。
考虑这一类, 与答案 的路径必定由 到 的一个祖先 ,再到 的其它子树中的 。
我们考虑找到最可能成为答案转折点的 结点,此时需要 到 其它子树中的深度最大的结点的距离最大。
给每个结点的子树标号,设为 ,则令 为除了 结点的第 个子树,子树中其它部分的深度最大值, 为对应的结点。预处理每个结点的前后缀算出。
令 为 的第 个子树,则 到 其它子树中的深度最大的结点的距离为 。考虑对每个 预处理出最优的 。这个可以开始的时候 一遍求出。
找到 之后,我们还要求出它除了子树 以外的一个 级儿子,也转化为 的 级祖先。注意判断无解。
关于线性求出 级祖先:考虑 过程中用栈记录所有的祖先,在找到询问时标答案为倒数第 位即可。
时间复杂度 。
带上小常数的 应该都放过了吧?
std:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=2e6+10;
struct Edge{
int to,nxt;
}a[N<<1];
int n,q,cnt,head[N];
inline void add(int u,int v){
cnt++;
a[cnt]={v,head[u]};
head[u]=cnt;
}
int d[N],f[N],g[N],*fp[N],*gp[N],son[N],pool[N<<2],*now=pool;
int pf[N],pg[N],sf[N],sg[N],stc[N];
inline void dfs1(int x,int fa){
d[x]=d[fa]+1,f[x]=d[x],g[x]=x;
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t==fa) continue;
dfs1(t,x);
son[x]++;
if(f[t]>f[x]) f[x]=f[t],g[x]=g[t];
}
}
int y[N],z[N];
inline void dfs2(int x,int fa,int py,int pz,int mx){
int sz=son[x],cnt=0;
fp[x]=now,now+=sz+1;
gp[x]=now,now+=sz+1;
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t==fa) continue;
stc[++cnt]=t;
}
assert(cnt==sz);
pf[0]=sf[sz+1]=0;
for(int i=1;i<=sz;i++)
pf[i]=sf[i]=f[stc[i]];
for(int i=1;i<=sz;i++){
if(pf[i]<=pf[i-1])
pf[i]=pf[i-1],pg[i]=pg[i-1];
else
pg[i]=g[stc[i]];
}
for(int i=sz;i>=1;i--){
if(sf[i]<=sf[i+1])
sf[i]=sf[i+1],sg[i]=sg[i+1];
else
sg[i]=g[stc[i]];
}
for(int i=1;i<=sz;i++){
if(pf[i-1]<=sf[i+1])
fp[x][i]=sf[i+1],gp[x][i]=sg[i+1];
else
fp[x][i]=pf[i-1],gp[x][i]=pg[i-1];
}
if(sz==1){
fp[x][1]=d[x],gp[x][1]=x;
}
y[x]=py,z[x]=pz,cnt=0;
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t==fa) continue;
cnt++;
if(fp[x][cnt]-2*d[x]>mx)
dfs2(t,x,gp[x][cnt],x,fp[x][cnt]-2*d[x]);
else
dfs2(t,x,py,pz,mx);
}
}
int ans[N],stk[N],top,cnt2,head2[N],nxt[N],ed[N];
struct Query{
int k,id;
}w[N];
inline void add2(int x,Query t){
cnt2++;
if(!head2[x]) head2[x]=cnt2;
nxt[ed[x]]=cnt2;
ed[x]=cnt2,w[cnt2]=t;
}
inline void dfs3(int x,int fa){
stk[++top]=x;
for(int i=head2[x];i;i=nxt[i]){
Query tmp=w[i];
if(top<=tmp.k) ans[tmp.id]=-1;
else ans[tmp.id]=stk[top-tmp.k];
}
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
if(t==fa) continue;
dfs3(t,x);
}
top--;
}
int main(){
n=read(),q=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs1(1,0);
dfs2(1,0,1,1,-1e9);
for(int i=1;i<=q;i++){
int x=read(),k=read();
if(d[x]>k) add2(x,{k,i});
else if(f[x]-d[x]>=k) add2(g[x],{f[x]-d[x]-k,i});
else{
int py=y[x],pz=z[x];
if(d[x]+d[py]-2*d[pz]<k) ans[i]=-1;
else add2(py,{d[py]-k+d[x]-2*d[pz],i});
}
}
dfs3(1,0);
for(int i=1;i<=q;i++)
write(ans[i]),putc('\n');
flush();
}
好的,被验题人薄纱了。
有一个结论:树上距离点 最远的点一定是直径两端点之一,所以把直径提出来讨论一下就好了。
时间复杂度 。
再见 qwq~
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现