「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\le R_i\land r-k+1\ge L_i\\ k\le R_i-L_i+1\land l+k-1\le R_i\le r \]

我们分别对 \(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]);
}
posted @ 2025-07-10 21:53  LastKismet  阅读(31)  评论(0)    收藏  举报