[Ynoi2000] Tree Move One Step To Node Range Query

题面

题目描述

给定 \(n\) 个顶点的树,顶点编号为 \(1,\dots,n\),给定长度 \(n_0\) 的序列 \(a_1,\dots,a_{n_0}\),共 \(m\) 次查询,每次查询给定 \(l,r,x\),问树的顶点 \(x\),依次向 \(a_l,\dots,a_r\) 移动一步,到达的顶点。

\(x=y\),则从顶点 \(x\)\(y\) 移动一步到达 \(x\),否则到达与 \(x\) 在树上相邻且距离 \(y\) 最近的位置。

数据范围

  • \(1\leq n,n_0,m\leq 10^6\)

  • 时间限制:12s。

题解

相当于区间复合函数,考虑离线做扫描线,发现操作 \(a_i\) 相当于让 \(a_i\) 到根的路径上每个点向下移动一步,其余的点向父亲移动一步。

考虑重链剖分,对于每条重链开一颗平衡树,发现向上向下的操作需要维护重链之间的变换。对于到根路径上的向下操作,考虑打 tag,向下变换重链的点直接暴力做即可。而对于整体向上的操作,全局打向上 tag 显然不现实,考虑维护每条重链中最早通过全局向上变换重链的时间点,并把它压进一个可删堆里。同时需要保证平衡树时刻保持每个点只有 \(\leq 1\) 个。

关于时间复杂度,每个节点如果只算向上操作最多变换 \(O(\log n)\) 条重链,而每次向下操作只会影响 \(O(\log n)\) 个节点,于是变换重链次数的总量是 \(O(n\log n)\) 的,时间复杂度 \(O(n\log^2 n)\)

#include<bits/stdc++.h>

using namespace std;

#define fir(x) (x).first
#define sec(x) (x).second
#define endl '\n'
using pii=pair<int,int>;
const int N=1e6+9;
const int inf=1e9+7;
template<class S,class T> inline void ChMax(S &x,T y){if(y>x) x=y;}
template<class S,class T> inline void ChMin(S &x,T y){if(y<x) x=y;}

mt19937 rng(4649);

int fi[N],ne[N<<1],to[N<<1],adj;
inline void AddEdge(int x,int y){
    ne[++adj]=fi[x];
    fi[x]=adj;
    to[adj]=y;
}

int siz[N],fa[N],hson[N],dep[N];
inline void DFS1(int x){
    siz[x]=1;
    for(int i=fi[x];i;i=ne[i]){
        int y=to[i];
        if(y==fa[x]) continue ;
        fa[y]=x;
        dep[y]=dep[x]+1;
        DFS1(y);
        siz[x]+=siz[y];
        if(siz[y]>siz[hson[x]]) hson[x]=y;
    }
}
int top[N],dfn[N],idfn[N],dcnt;
inline void DFS2(int x,int t){
    dfn[x]=++dcnt;
    idfn[dcnt]=x;
    top[x]=t;
    if(hson[x]) DFS2(hson[x],t);
    else return ;
    for(int i=fi[x];i;i=ne[i]){
        int y=to[i];
        if(y==fa[x]) continue ;
        if(y==hson[x]) continue ;
        DFS2(y,y);
    }
}

int f[N<<2];
inline void InitDSU(int lim){for(int i=1;i<=lim;i++) f[i]=i;}
inline int Find(int x){return f[x]==x?x:f[x]=Find(f[x]);}
inline void Union(int x,int y){f[Find(y)]=Find(x);}

struct Node{
    int lc,rc,fa,dat,tag,qid,prio;
}tr[N<<2];
#define lc(x) tr[x].lc
#define rc(x) tr[x].rc
#define fa(x) tr[x].fa
#define dat(x) tr[x].dat
#define tag(x) tr[x].tag
#define qid(x) tr[x].qid
#define prio(x) tr[x].prio

int cnt;
inline int Allc(){return ++cnt;}
inline void Push(int x,int k){dat(x)+=k,tag(x)+=k;}
inline void PushDown(int x){
    if(lc(x)) Push(lc(x),tag(x));
    if(rc(x)) Push(rc(x),tag(x));
    tag(x)=0;
}
inline void PushUp(int x){
    fa(x)=0;
    if(lc(x)) fa(lc(x))=x;
    if(rc(x)) fa(rc(x))=x;
}
inline int Merge(int x,int y){
    if(!x||!y) return x|y;
    if(prio(x)>prio(y)){
        PushDown(x);
        rc(x)=Merge(rc(x),y);
        return PushUp(x),x;
    }else{
        PushDown(y);
        lc(y)=Merge(x,lc(y));
        return PushUp(y),y;
    }
}
inline pii Split(int x,int k){
    if(!x) return {0,0};
    PushDown(x);
    if(k>=dat(x)){
        pii res=Split(rc(x),k);
        rc(x)=fir(res);
        PushUp(x);
        return {x,sec(res)};
    }else{
        pii res=Split(lc(x),k);
        lc(x)=sec(res);
        PushUp(x);
        return {fir(res),x};
    }
}
inline int FindL(int x){while(lc(x)) PushDown(x),x=lc(x);return x;}
inline int FindR(int x){while(rc(x)) PushDown(x),x=rc(x);return x;}
inline int Join(int x,int y){
    if(!x||!y) return x|y;
    int u=FindR(x),v=FindL(y);
    if(dat(u)==dat(v)){
        y=sec(Split(y,dat(v)));
        Union(qid(u),qid(v));
    }else assert(dat(u)<dat(v));
    return Merge(x,y);
}
inline int Insert(int &rt,int k,int id){
    int x=Allc();
    dat(x)=k,qid(x)=id,prio(x)=rng();
    pii tmp=Split(rt,k);
    rt=Merge(Join(fir(tmp),x),sec(tmp));
    return x;
}
inline void PushAll(int x){
    if(!x) return ;
    PushAll(fa(x));
    PushDown(x);
}

int a[N],ql[N],qr[N],qx[N],nid[N],ans[N],n,m,q;
vector<int> st[N],ed[N];

int lst[N],rt[N];
set<pii> hp;
inline void Erase(int x){
    if(lst[x]) hp.erase({lst[x],x}),lst[x]=0;
}
inline void Insert(int x){
    if(rt[x]){
        lst[x]=dat(FindL(rt[x]))-dfn[x]+1;
        hp.insert({lst[x],x});
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);

    cin>>n>>m>>q;
    for(int i=2;i<=n;i++) cin>>fa[i],AddEdge(fa[i],i);
    for(int i=1;i<=m;i++) cin>>a[i];
    for(int i=1;i<=q;i++){
        cin>>ql[i]>>qr[i]>>qx[i];
        st[ql[i]].push_back(i);
        ed[qr[i]].push_back(i);
    }
    
    InitDSU(q);
    DFS1(1),DFS2(1,1);
    for(int t=0;t<m;){
        for(int i:st[t+1]){
            Erase(top[qx[i]]);
            nid[i]=Insert(rt[top[qx[i]]],dfn[qx[i]]+t,i);
            Insert(top[qx[i]]);
        }
        int u=a[t+1],v=a[t+1];
        while(u){
            Erase(top[u]);
            pii tmp=Split(rt[top[u]],dfn[u]+t);
            rt[top[u]]=sec(tmp);
            tmp=Split(fir(tmp),dfn[u]+t-1);
            Push(fir(tmp),2);
            if(sec(tmp)){
                int x=sec(tmp);
                tag(x)=0,dat(x)=dfn[v]+t+1;
                rt[top[v]]=Join(x,rt[top[v]]);
            }
            rt[top[u]]=Join(fir(tmp),rt[top[u]]);
            u=fa[v=top[u]];
        }
        t++;
        while(hp.size()&&fir(*hp.begin())==t){
            int p=sec(*hp.begin()),q=top[fa[p]];
            Erase(p),Erase(q);
            pii tmp=Split(rt[p],t+dfn[p]-1);
            rt[p]=sec(tmp);
            int x=fir(tmp);
            tag(x)=0,dat(x)=dfn[fa[p]]+t;
            tmp=Split(rt[q],dfn[fa[p]]+t);
            rt[q]=Merge(Join(fir(tmp),x),sec(tmp));
            Insert(p),Insert(q);
        }
        u=top[a[t]];
        while(u) Insert(u),u=top[fa[u]];
        for(int i:ed[t]){
            int x=nid[Find(i)];
            PushAll(x);
            ans[i]=idfn[dat(x)-t];
        }
    }

    for(int i=1;i<=q;i++) cout<<ans[i]<<endl;
    
    return 0;
}
posted @ 2025-04-11 17:03  JoeyJiang  阅读(27)  评论(0)    收藏  举报