题解:P13625 [ICPC 2024 APC] Tree Quiz
在这里先规定记 \(L\) 表示 \(\text{LCA}(x,y)\)。
容易发现伪代码里放进序列里的数在 \(n\) 进制下是一个三位数(不足三位在高位补零),并且从左往右分别为 \(x-1\),\(L\),\(y-1\),所以对这个序列排序等价于按这三个关键字排序。
对于一个 \(k\),容易先求出对应的 \(x\)。现在考虑求对应的 \(L\) 是什么。考虑将询问离线并挂到相应的点 \(x\) 上,\(L\) 一定是 \(x\) 在树上的祖先,所以考虑遍历一遍树,当遍历到 \(x\) 时要保证对于其所有祖先对应的满足 \(L\) 等于它的 \(y\) 的数量都被求出来。当前遍历到的点从 \(fa_x\) 变为 \(x\) 时,数量变化的值会更改到 \(fa_x\) 和 \(x\),从 \(x\) 退回 \(fa_x\) 时同理,变化的值就是 \(siz_x\),原因是走到 \(x\) 后以 \(x\) 为根的子树与 \(x\) 的最近公共祖先从 \(fa_x\) 变为了 \(x\),其余的点不变。
然后考虑用线段树维护,求 \(L\) 就在线段树上跑线段树二分即可。
先考虑 \(x\) 不为 \(L\) 的情况。记 \(L\) 的所有儿子中,在 \(x\) 到 \(L\) 的路径上的点为 \(s\)。那么 \(y\) 就一定在以 \(L\) 为根的子树的点构成的点集去掉以 \(s\) 为根的子树中的点后得到的点集中。考虑用 dfs 序表示点,此时这个点集会被划分为两个区间(可能有一个区间是空区间,如果写法刚好规避不掉的话再特判一下就行)。
如果 \(x\) 是 \(L\) 的话,则 \(y\) 一定在以 \(x\) 为根的子树当中,也是考虑用 dfs 序去表示这个子树,表示为一段区间。
于是现在问题变成了求区间第 \(k\) 小问题,直接上可持久化线段树二分即可。
板子部分不作讲解。
CODE:
#include<bits/stdc++.h>
#define ll long long
#define lc k<<1
#define rc k<<1|1
using namespace std;
int n,q;
int rt;
vector<int> g[100010];
int siz[100010],dfn[100010],tt=0,ddy[100010];
ll xw[100010];
vector<int> dy[100010];
int tr[400010];
struct aaa{int cl,cr,sum;}xds[3000010];
int tot=0,Rt[100010];
int las[100010];
ll ans[100010];
inline void up(int k){tr[k]=tr[lc]+tr[rc];}
inline void up1(int k){xds[k].sum=xds[xds[k].cl].sum+xds[xds[k].cr].sum;}
inline void add(int l,int r,int pos,int k,int c){if(l==r){tr[k]+=c;return;}int mid=(l+r)>>1;if(pos<=mid) add(l,mid,pos,lc,c);else add(mid+1,r,pos,rc,c);up(k);}
inline pair<int,int> query(int l,int r,int k,int c){if(l==r) return {l,c};int mid=(l+r)>>1;if(c<=tr[lc]) return query(l,mid,lc,c);return query(mid+1,r,rc,c-tr[lc]);}
inline void build(int l,int r,int &k){tot++,k=tot;if(l==r){xds[k].sum=0;return;}int mid=(l+r)>>1;build(l,mid,xds[k].cl),build(mid+1,r,xds[k].cr);}
inline void Insert(int l,int r,int pos,int his,int &k,int c){tot++,k=tot;if(l==r){xds[k].sum=xds[his].sum+c;return;}int mid=(l+r)>>1;if(pos<=mid) Insert(l,mid,pos,xds[his].cl,xds[k].cl,c),xds[k].cr=xds[his].cr;else Insert(mid+1,r,pos,xds[his].cr,xds[k].cr,c),xds[k].cl=xds[his].cl;up1(k);}
inline int query1(int l,int r,int ql1,int qr1,int ql2,int qr2,int c){if(l==r) return l;int mid=(l+r)>>1,ss=(xds[xds[qr1].cl].sum-xds[xds[ql1].cl].sum)+(xds[xds[qr2].cl].sum-xds[xds[ql2].cl].sum);if(c<=ss) return query1(l,mid,xds[ql1].cl,xds[qr1].cl,xds[ql2].cl,xds[qr2].cl,c);return query1(mid+1,r,xds[ql1].cr,xds[qr1].cr,xds[ql2].cr,xds[qr2].cr,c-ss);}
inline void dfs(int k){tt++,dfn[k]=tt,ddy[tt]=k,siz[k]=1;for(register int i=0;i<g[k].size();i++){int x=g[k][i];dfs(x),siz[k]+=siz[x];}}
inline void dfs1(int k)
{
for(register int i=0;i<dy[k].size();i++)
{
int x=dy[k][i];int c=(xw[x]-1)%(1ll*n)+1;pair<int,int> pp=query(1,n,1,c);int L=pp.first;c=pp.second;int y=0;
if(L==k) y=query1(1,n,Rt[dfn[k]-1],Rt[dfn[k]+siz[k]-1],Rt[dfn[k]+siz[k]-1],Rt[dfn[k]+siz[k]-1],c);
else y=query1(1,n,Rt[dfn[L]-1],Rt[dfn[las[L]]-1],Rt[dfn[las[L]]+siz[las[L]]-1],Rt[dfn[L]+siz[L]-1],c);
ans[x]=1ll*(k-1)*n*n+1ll*(L-1)*n+1ll*(y-1);
}
for(register int i=0;i<g[k].size();i++){int x=g[k][i];add(1,n,k,1,-siz[x]),add(1,n,x,1,siz[x]),las[k]=x,dfs1(x),las[k]=0,add(1,n,x,1,-siz[x]),add(1,n,k,1,siz[x]);}
}
int main()
{
scanf("%d%d",&n,&q);
for(register int i=1;i<=n;i++)
{
int fa;
scanf("%d",&fa);
if(!fa) rt=i;
else g[fa].push_back(i);
}
dfs(rt);
build(1,n,Rt[0]);
for(register int i=1;i<=n;i++) Insert(1,n,ddy[i],Rt[i-1],Rt[i],1);
for(register int i=1;i<=q;i++) scanf("%lld",&xw[i]),dy[(xw[i]-1)/(1ll*n)+1].push_back(i);
add(1,n,rt,1,siz[rt]),dfs1(rt),add(1,n,rt,1,-siz[rt]);
for(register int i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号