题解: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;
}

浙公网安备 33010602011771号