「NOIP2024-树上查询」题解
P11364 [NOIP2024] 树上查询
sol
考虑连续区间 \(\mathrm{dep}_{\mathrm{LCA}(L,R)}\),其为 \(\min\limits_{i=L}^{R-1}\mathrm{dep}_{\mathrm{LCA}(i,i+1)}\)。证明是简单的,对于区间的 LCA,其必然存在至少两个节点位于其不同子树处,必然存在一个 \(i\) 使得 \(i,i+1\) 跨越了子树。这是显然的。
现在考虑查询操作,查询一个区间内长为 \(k\) 的子区间的最大 \(dep\)。对一个区间考虑其所有子区间有点麻烦,考虑转化问题为,找到一个最大的 \(dep\),使得 \(\left[l,r\right]\) 内存在一个长为 \(k\) 的序列的区间 LCA 深度为 \(dep\)。
之所以这么转化,是因为不难发现只有 \(n-1\) 个不同的 \(dep\),也就是所有相邻两个点的 \(\mathrm{dep}_{\mathrm{LCA}}\)。那么也容易找到每个相邻点产生的 \(dep\) 的最大作用区间,记录下每个相邻点产生的 \(dep\) 然后单调栈一下即可,关键就是这个区间必然不包含一个产生的 \(dep\) 大于当前 \(dep\) 的相邻点对,那么单调栈找到上一个大于当前 \(dep\) 的相邻点对即可。向两个方向做两次就能找到极大作用范围。
现在问题变成,我们拥有一些有价值的线段,每次查询一个区间,求与之交集至少为 \(k\) 的价值最大的线段。
令线段 \(i\) 的左右端点为 \(L_i,R_i\) 且价值为 \(V_i\),那么一次查询 \(l,r,k\) 满足条件的线段必然满足下列两式之一(经过一系列推导可得):
我们分别对 \(r,k\) 扫描线即可,分别计算两种答案然后取 \(\max\)。
具体地,以 \(r\) 为例,从大到小扫,把当前所有 \(R_i=r\) 的新的满足左侧条件的线段加入答案,也就是在 \(L_i\) 处更新上 \(V_i\)。然后扫到相应 \(r\) 的查询时,查询所有 \(\le r-k+1\) 的 \(L\) 最大的 \(V\) 即可。
也就是说我们通过扫描线处理左侧限制,再通过线段树区间查询处理右侧限制。二式同理。
然后有一个小问题,倘若一个与要求区间相交的线段,其与区间的交集的 LCA 并非整条线段的 LCA 怎么办,这个显然不成问题,因为必然存在那个更优的 LCA 相应的线段,也必然会被后续计算到。当前这个不合法的答案显然不优。
这里我们是通过相邻两点的 LCA 深度来更新答案的,因此需要特判一下 \(k=1\) 的查询。这个直接区间查深度最小值即可。
code
const int N=1e6+5,T=21;
int n,q;
vec<int> g[N];
int dp[N];
int dfn[N],dcnt,id[N],to[N];
int st[N][T],cnt;
void dfs(int now,int fid){
dfn[now]=++dcnt,to[dcnt]=now;
st[++cnt][0]=dfn[now],id[now]=cnt;
dp[now]=dp[fid]+1;
for(auto nxt:g[now]){
if(nxt==fid)continue;
dfs(nxt,now);
st[++cnt][0]=dfn[now];
}
}
inline int lca(int a,int b){
a=id[a],b=id[b];
if(a>b)swap(a,b);
int t=__lg(b-a+1);
return to[min(st[a][t],st[b-(1<<t)+1][t])];
}
int v[N];
struct node{int x,y,v;}ns[N];
struct query{int l,r,k,id;}qs[N];
int lst[N];
int stk[N],top;
void solve(){
stk[top=1]=0;v[0]=-inf;
repl(i,1,n){
while(v[stk[top]]>=v[i])--top;
lst[i]=stk[top]+1;
stk[++top]=i;
}
}
struct segment{
int dat[N<<2],laz[N<<2];
void build(int x=1,int l=1,int r=n){
dat[x]=laz[x]=0;
if(l==r)return;
int m=l+r>>1;
build(x<<1,l,m);
build(x<<1|1,m+1,r);
}
void pushdown(int x){
chmax(dat[x<<1],laz[x]);
chmax(dat[x<<1|1],laz[x]);
chmax(laz[x<<1],laz[x]);
chmax(laz[x<<1|1],laz[x]);
}
void update(int x){dat[x]=max(dat[x<<1],dat[x<<1|1]);}
void modify(int lq,int rq,int v,int x=1,int l=1,int r=n){
if(lq<=l&&r<=rq){
chmax(dat[x],v);
chmax(laz[x],v);
return;
}
pushdown(x);
int m=l+r>>1;
if(lq<=m)modify(lq,rq,v,x<<1,l,m);
if(m<rq)modify(lq,rq,v,x<<1|1,m+1,r);
update(x);
}
int query(int lq,int rq,int x=1,int l=1,int r=n){
if(lq<=l&&r<=rq)return dat[x];
pushdown(x);
int m=l+r>>1;
int res=0;
if(lq<=m)chmax(res,query(lq,rq,x<<1,l,m));
if(m<rq)chmax(res,query(lq,rq,x<<1|1,m+1,r));
return res;
}
}seg;
vec<node> vn[N];
vec<query> vq[N];
int ans[N];
inline void Main(){
read(n);
repl(i,1,n){
int a,b;read(a,b);
g[a].pub(b),g[b].pub(a);
}
dfs(1,0);
repl(t,1,T)rep(i,1,cnt-(1<<t)+1)st[i][t]=min(st[i][t-1],st[i+(1<<t-1)][t-1]);
repl(i,1,n)ns[i].v=v[i]=dp[lca(i,i+1)];
solve();
repl(i,1,n)ns[i].x=lst[i];
reverse(v+1,v+n);
solve();
repl(i,1,n)ns[i].y=n-lst[n-i]+1,vn[ns[i].y].pub(ns[i]);
read(q);
rep(i,1,q)qs[i].id=i,read(qs[i].l,qs[i].r,qs[i].k);
rep(i,1,q)if(qs[i].k>1)vq[qs[i].r].pub(qs[i]);
seg.build();
per(r,n,1){
for(auto i:vn[r])seg.modify(i.x,i.x,i.v);
for(auto i:vq[r])chmax(ans[i.id],seg.query(1,i.r-i.k+1));
}
rep(i,1,n)vn[i].clear(),vq[i].clear();
repl(i,1,n)vn[ns[i].y-ns[i].x+1].pub(ns[i]);
rep(i,1,q)if(qs[i].k>1)vq[qs[i].k].pub(qs[i]);
seg.build();
per(k,n,1){
for(auto i:vn[k])seg.modify(i.y,i.y,i.v);
for(auto i:vq[k])chmax(ans[i.id],seg.query(i.l+i.k-1,i.r));
}
seg.build();
rep(i,1,n)seg.modify(i,i,dp[i]);
rep(i,1,q)if(qs[i].k==1)ans[i]=seg.query(qs[i].l,qs[i].r);
rep(i,1,q)put(ans[i]);
}

浙公网安备 33010602011771号