题解:CF226E Noble Knight's Path

前言

本文同步自洛谷专栏,题目传送门

这道远古的黑题心眼子还是比较少的。

分析

基本思路

结合数据范围和树上路径问题,容易想到使用重链剖分。

时间一维限制可以利用主席树按 dfn 序维护。

像这种规定了顺序的树剖问题,一种最暴力的方法就是把所有区间用若干个栈或者队列存下。

修改操作是容易实现的,直接插入主席树即可,下面重点看查询。

解决问题

首先处理不含起点 \(u\) 和终点 \(v\) 的条件,求一个 \(g=\operatorname{LCA}(u,v)\),然后特判 \(u,v\) 之间距离是否 \(\le 1\),(一个小坑点)。

再判断 \(u,v\) 是否 \(=g\)

如果 \(u=g\),则将 \(u\) 设定为 \(v\) 的深度为 \(dep_u+1\) 的祖先,这一步可以对每一条重链开一个 vector,借助重剖 \(O(\log{n})\) 查询;否则直接向上跳一步即可。\(v\) 同理。

下文中 \(u,v\) 均指转化后的 \(u,v\)

此时像正常树剖求 LCA 一样向上跳,只不过不马上查询,而是存下区间。

称从 \(u\) 向上跳为上行,从 \(v\) 向上跳的为下行(实际上是从 LCA 向下到 \(v\))。

则上行过程中的 dfn 区间存入一个队列,下行的存入一个栈。(建议手写队列和栈,简化代码实现)

然后开始逐个考虑,先考虑队列中的,再考虑栈中的。

每一个 dfn 区间对应 \(O(\log{n})\) 个线段树区间,也可以用一个双端队列存下,统一处理。

上行过程中,dfn 从大到小,要在双端队列末尾操作;下行过程在开头。

每次处理一个线段树区间时,比较对应的未被污染的点数 \(d\) 与当前的 \(k\)

\(k\le d\),则直接进入这个线段树区间二分,可以传一个额外的参数表示先考虑 dfn 大的还是 dfn 小的,返回对应值(不要传回 dfn!);

否则 \(k-d\),考虑下一个区间。

最后若 \(k>0\),返回无解即可。

实现

\(n,m\) 同阶,时间复杂度 \(O(n\log^2{n})\),空间复杂度 \(O(n\log{n})\)

队列、双端队列、栈这些数据结构只是用于方便理解,具体实现时,手写会更方便。

$\red{\text{code}}$
#include<bits/stdc++.h>
using namespace std;
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,IO,stdin)),*rp1++)
#define N 100005
#define ls(a) tr[a].ch[0]
#define rs(a) tr[a].ch[1]
#define ct(a) tr[a].cnt
#define mid ((tr[u].l+tr[u].r)>>1)
#define F first
#define S second
#define delta(u,v) tr[u].r-tr[u].l+1-tr[u].cnt+tr[v].cnt
//delta 表示两个主席树结点的未污染城堡数的结果
struct node{//存左右端点会比较方便
    int ch[2],cnt,l,r;
}tr[N*20];

const int IO=1<<22;
char buf[IO+2],*rp1,*rp2;
int n,m,root,tot,tail,fa[N],son[N];
int dfn[N],dep[N],tp[N],rt[N],id[N];
vector<int>E[N],L[N];//L 记录重链信息
pair<int,int>Q[50],stk[2][30];
//记录访问到的主席树结点对,记录树剖 dfn 区间
inline int read(){
    int a=0,c=gc();
    while(!isdigit(c)) c=gc();
    while(isdigit(c)) a=10*a+c-'0',c=gc();
    return a;
}

void dfs1(int u){
    tp[u]=1;
    for(auto &v:E[u]){
        dep[v]=dep[u]+1,fa[v]=u,dfs1(v),tp[u]+=tp[v];
        if(tp[v]>tp[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int top){
    dfn[u]=++tot,L[tp[u]=top].emplace_back(u),id[tot]=u;
    if(son[u]) dfs2(son[u],top);
    for(auto &v:E[u]){
        if(v!=son[u]) dfs2(v,v);
    }
}

void build(int &u,int l,int r){
    u=++tot,tr[u].l=l,tr[u].r=r,ct(u)=r-l+1;
    if(tr[u].l==tr[u].r) return;
    build(ls(u),l,mid),build(rs(u),mid+1,r);
}

void insert(int u,int &p,int q){
    p=++tot,tr[p]=tr[u],ct(p)--;
    if(tr[u].l==tr[u].r) return;
    if(q<=mid) insert(ls(u),ls(p),q);
    else insert(rs(u),rs(p),q);
}

inline int LCA(int u,int v){
    for(;tp[u]!=tp[v];u=fa[tp[u]]){
        if(dep[tp[u]]<dep[tp[v]]) u^=v^=u^=v;
    }
    return dep[u]<dep[v]?u:v;
}

inline int kth(int u,int d){//求 u 的深度为 d 的祖先
    while(dep[tp[u]]>d) u=fa[tp[u]];
    return L[tp[u]][d-dep[tp[u]]];
}

void Sum(int u,int v,int ql,int qr){
    if(ql<=tr[u].l&&tr[u].r<=qr) return Q[++tail]={u,v},void();
    if(ql<=mid) Sum(ls(u),ls(v),ql,qr);
    if(mid<qr) Sum(rs(u),rs(v),ql,qr);
}

int query(int u,int v,int k,int o){//o=1 表示从大向小考虑
    for(int d;tr[u].l<tr[u].r;){
        d=delta(tr[u].ch[o],tr[v].ch[o]);
        if(d>=k) v=tr[v].ch[o],u=tr[u].ch[o];
        else v=tr[v].ch[o^1],u=tr[u].ch[o^1],k-=d;
    }
    return id[tr[u].l];
}

int Query(int t){
    int u=read(),v=read(),k=read(),Y=read();
    int g=LCA(u,v),o=0,top[2]={0,0};//清除起点和终点的影响
    if(dep[u]+dep[v]-2*dep[g]<=1) return -1;
    if(g==u) u=kth(v,dep[u]+1),v=fa[v],g=u;
    else if(g!=v) v=fa[v],u=fa[u];
    else v=kth(u,dep[v]+1),u=fa[u],g=v;
    int x=u,y=v;
    for(;tp[x]!=tp[y];x=fa[tp[x]]){//利用 o 减少判断
        if(dep[tp[x]]<dep[tp[y]]) x^=y^=x^=y,o^=1;
        stk[o][++top[o]]={dfn[tp[x]],dfn[x]};//暴力存
    }
    if(dep[x]<dep[y]) x^=y^=x^=y,o^=1;
    stk[o][++top[o]]={dfn[y],dfn[x]};
    for(int i=1;i<=top[0];i++){//dfn 大->dfn 小
        tail=0,Sum(rt[Y],rt[t],stk[0][i].F,stk[0][i].S);
        for(int j=tail,d;j>=1;k-=d,j--){
            d=delta(Q[j].F,Q[j].S);
            if(d>=k) return query(Q[j].F,Q[j].S,k,1);
        }
    }
    for(int i=top[1];i>=1;i--){
        tail=0,Sum(rt[Y],rt[t],stk[1][i].F,stk[1][i].S);
        for(int j=1,d;j<=tail;k-=d,j++){
            d=delta(Q[j].F,Q[j].S);
            if(d>=k) return query(Q[j].F,Q[j].S,k,0);
        }
    }
    return -1;
}

int main(){
    n=read();
    for(int i=1;i<=n;i++){
        E[fa[i]=read()].emplace_back(i);
        if(fa[i]==0) root=i;
    }
    dfs1(root),dfs2(root,root),tot=0;
    build(rt[0],1,n),m=read();
    for(int i=1,o;i<=m;i++){
        o=read(),rt[i]=rt[i-1];
        if(o==1) insert(rt[i-1],rt[i],dfn[read()]);
        else printf("%d\n",Query(i));
    }
    return 0;
}


posted @ 2026-06-05 11:09  Wxb2010  阅读(5)  评论(0)    收藏  举报