NOIP2024 树上查询 题解
IvanZhang2009 · · 题解
来一个考场做法和后续优化。
首先有一个
可以发现多个点的 lca 是可合并可重信息,于是可以用 st 表维护区间 lca。
于是可以得到一个
看到剩下的除了正解就是链了,于是考虑链。我们不妨直接把链考虑成序列:
这种区间一看就非常符合直觉,看上去非常有优化前途啊!我们不妨设
朴素做,对于一个有用区间
考虑是否可以扩展到树上。注意到链可做的原因是,lca 可以表示为区间
zhuzhu2891 告诉我每个区间只需要保留前后缀即可。具体地,在三维偏序的
#include<bits/stdc++.h>
#define REP(i,a,n) for(int i=(a);i<(int)(n);++i)
#define pb push_back
using namespace std;
int read(){
int res=0;char c=getchar();
while(c<48||c>57)c=getchar();
do res=(res<<1)+(res<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
return res;
}
struct queries{
int l,r,id;
};
struct ds{
int seg[2000005];
void build(int l,int r,int p){
seg[p]=0;
if(l==r)return;
int m=(l+r)>>1;
build(l,m,p*2+1);build(m+1,r,p*2+2);
}
void update(int pos,int l,int r,int p,int val){
seg[p]=max(seg[p],val);
if(l==r)return;
int m=(l+r)>>1;
if(m>=pos)update(pos,l,m,p*2+1,val);
else update(pos,m+1,r,p*2+2,val);
}
int query(int l,int r,int s,int t,int p){
if(l<=s&&t<=r)return seg[p];
int m=(s+t)>>1,res=0;
if(m>=l)res=query(l,r,s,m,p*2+1);
if(m<r)res=max(res,query(l,r,m+1,t,p*2+2));
return res;
}
}s1,s2;
int n,q;
vector<int>v[500005];
int an[22][500005],fa[500005],dep[500005],a[500005];
int st[22][500005],mx[22][500005],dfn[500005];
int tot;
int ans[500005];
vector<queries>qr[500005],add[500005];
int getmax(int x,int y){return dep[x]<dep[y]? x:y;}
void dfs(int x,int pre,int d){
fa[x]=pre;dfn[x]=tot++;an[0][dfn[x]]=pre;dep[x]=d;
for(auto i:v[x])if(i!=pre)dfs(i,x,d+1);
}
int getlca(int x,int y){
if(x==y)return x;
x=dfn[x];y=dfn[y];if(x>y)swap(x,y);
int s=__lg(y-x);
return getmax(an[s][x+1],an[s][y-(1<<s)+1]);
}
int query(int l,int r){
int s=__lg(r-l+1);
return dep[getlca(st[s][l],st[s][r-(1<<s)+1])]+1;
}
int qmax(int l,int r){
int s=__lg(r-l+1);
return max(mx[s][l],mx[s][r-(1<<s)+1]);
}
signed main(){
freopen("query.in","r",stdin);
freopen("query.out","w",stdout);
n=read();
REP(i,1,n){
int x=read()-1,y=read()-1;
v[x].pb(y);v[y].pb(x);
}
dfs(0,-1,0);
REP(j,0,__lg(n-1)){
REP(i,1,n-(1<<(j+1))+1)an[j+1][i]=getmax(an[j][i],an[j][i+(1<<j)]);
}
REP(i,0,n)st[0][i]=i,mx[0][i]=dep[i]+1;
REP(j,0,__lg(n)){
REP(i,0,n-(1<<(j+1))+1)st[j+1][i]=getlca(st[j][i],st[j][i+(1<<j)]);
REP(i,0,n-(1<<(j+1))+1)mx[j+1][i]=max(mx[j][i],mx[j][i+(1<<j)]);
}
REP(i,0,n-1)a[i]=dep[getlca(i,i+1)];
stack<int>st;
REP(i,0,n-1){
while(!st.empty()&&a[st.top()]>a[i])st.pop();
if(!st.empty()){
int x=st.top()+1;
if(x<i){
queries y={x,i-1,query(x,i)};
add[i-x].pb(y);
}
}
st.push(i);
}
while(!st.empty())st.pop();
for(int i=n-2;i>=0;--i){
while(!st.empty()&&a[st.top()]>a[i])st.pop();
if(!st.empty()){
int x=st.top()-1;
if(x>i){
queries y={i+1,x,query(i+1,x+1)};
add[x-i].pb(y);
}
}
st.push(i);
}
q=read();
REP(i,0,q){
int l=read()-1,r=read()-1,k=read();
if(k==1)ans[i]=qmax(l,r);
else{
ans[i]=max(query(l,l+k-1),query(r-k+1,r));
--r;--k;
qr[k].pb({l,r,i});
}
}
--n;
s1.build(0,n-1,0);s2.build(0,n-1,0);
for(int i=n;i>=1;--i){
for(auto j:add[i]){
s1.update(j.l,0,n-1,0,j.id);
s2.update(j.r,0,n-1,0,j.id);
}
for(auto j:qr[i]){
ans[j.id]=max(ans[j.id],s1.query(j.l,j.r-i+1,0,n-1,0));
ans[j.id]=max(ans[j.id],s2.query(j.l+i-1,j.r,0,n-1,0));
}
}
REP(i,0,q)cout<<ans[i]<<"\n";
cerr<<clock()<<endl;
return 0;
}