2024 ICPC Asia Pacific Championship-K-线段树合并or主席树
比赛链接:https://codeforces.com/contest/1938
给一棵有根树,执行以下代码:
let L be an empty array
for x = 1 to n
for y = 1 to n
append ((x - 1) * n * n + (LCA(x, y) - 1) * n + (y - 1)) to L
sort L in non-decreasing order
然后进行 \(q\) 次询问,每次问 \(L\) 中第 \(k\) 个元素是多少。\(1\leq n,q\leq 10^5\).
\(L\) 中的元素可以看成 \(n\) 进制数,每个 \(x\) 出现次数一样,所以先确定 \(x=\lfloor (k-1)/n\rfloor +1\),然后\(k\to (k-1)\bmod n +1\),把询问写成序对 \((x,y,LCA,idx)\) 先挂到每个结点 \(x\) 上,根据下图所示查询 \(x\) 的每个祖先(包括 \(x\) )作为LCA,能有几个点,在这条链上二分能 \(O(n\log^2 n)\) 确定LCA,同时 \(k\to k-\sum_{i=1}^{l-1} f_i\)(其中 \(l\) 是确定的LCA在链上的位置,\(f_i\) 是每个点作为LCA能得到的 \(y\) 的个数,这些都容易处理);考虑到有 \(q\) 个询问,将询问离线下来,一边dfs一边处理。
最后确定 \(y\) ,再次dfs将询问挂在LCA上,在上一步中顺便需要对每个询问记录LCA往 \(x\) 放在往下走的第一个点 \(dir\) 是多少,求 \(y\) 的时候相当于查询 \(LCA\) 的子树扣掉 \(dir\) 的子树的这些结点中,第 \(k\) 大的结点是多少:
在每个节点上开一个动态线段树,结点 \(i\) 初始只有 \(v[i]=1\) ,dfs的时候从叶子往根节点一边回答询问一边合并线段树。因为处理LCA的时候不仅需要LCA的线段树,还需要某个孩子dir的线段树,所以需要对线段树做一个备份:一棵树在处理询问前合并,另一棵在处理询问后合并:
void dfs2(int x){
for(auto to:G[x])dfs2(to);
for(auto to:G[x])//merge segment tree 1
//处理x(其实是LCA)上的每个询问
for(auto to:G[x])//merge segment tree 2
}
代码:
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int LOG=65;
struct Query{
int x,y,LCA,k,idx,dir;
};
struct Fenwick{
int a[N],n;
void init(int _n){n=_n;}
int lowbit(int x){return x&-x;}
int query(int x){
int ret=0;
for(;x;x-=lowbit(x))ret+=a[x];
return ret;
}
void add(int x,int v){
for(;x<=n;x+=lowbit(x))a[x]+=v;
}
void modify(int x,int v){
int cur=query(x);
if(x)cur-=query(x-1);
add(x,v-cur);
}
}tr;
struct SegmentTree{
int rt[N],tr[N*LOG],lson[N*LOG],rson[N*LOG];
int bin[N*LOG],cb,cnt;
#define ls (lson[node])
#define rs (rson[node])
void del_node(int node){bin[++cb]=node;}
int new_node(){
if(cb)return bin[cb--];
return ++cnt;
}
void push_up(int node){
if(!ls)ls=new_node(),tr[ls]=0;
if(!rs)rs=new_node(),tr[rs]=0;
tr[node]=tr[ls]+tr[rs];
}
void modify(int &node,int l,int r,int x,int v){
if(!node)node=new_node();
if(l==r){
tr[node]=v;
return ;
}
int mid=(l+r)>>1;
if(mid>=x)modify(ls,l,mid,x,v);
else modify(rs,mid+1,r,x,v);
push_up(node);
}
int merge(int &u,int &v,int l,int r){
if(!u||!v)return u|v;
if(l==r){
tr[u]+=tr[v];
del_node(v);
return u;
}
int mid=(l+r)>>1;
lson[u]=merge(lson[u],lson[v],l,mid);
rson[u]=merge(rson[u],rson[v],mid+1,r);
push_up(u);
return u;
}
int query(int node,int l,int r,int ql,int qr){
if(!node)return 0;
if(ql<=l&&r<=qr)return tr[node];
int mid=(l+r)>>1,ret=0;
if(mid>=ql)ret+=query(ls,l,mid,ql,qr);
if(mid+1<=qr)ret+=query(rs,mid+1,r,ql,qr);
return ret;
}
}before,seg;
int n,q,p[N],sz[N],st;
int pos[N];
ll ans[N];
vector<int> path;
vector<vector<int>> G;
vector<vector<Query>> Q1,Q2;
void dfs0(int x){
sz[x]=1;
for(auto to:G[x]){
dfs0(to);
sz[x]+=sz[to];
}
}
void dfs1(int x){
path.push_back(x);
pos[x]=path.size()-1;
tr.modify(x,sz[x]);
for(auto itr:Q1[x]){
tr.modify(x,sz[x]);
int l=1,r=n,ret=-1;
while(l<=r){
int mid=(l+r)>>1;
if(tr.query(mid)>=itr.k){
r=mid-1;
ret=mid;
}else l=mid+1;
}
int dir=-1,k=itr.k-tr.query(ret-1);
if(ret!=x)dir=path[pos[ret]+1];
Q2[ret].push_back((Query){x,-1,ret,k,itr.idx,dir});
}
for(auto to:G[x]){
tr.modify(x,sz[x]-sz[to]);
dfs1(to);
}
tr.modify(x,0);
path.pop_back();
}
void dfs2(int x){
for(auto to:G[x])dfs2(to);
for(auto to:G[x])before.rt[x]=before.merge(before.rt[x],before.rt[to],1,n);
for(auto itr:Q2[x]){
int l=1,r=n,ret=-1;
while(l<=r){
int mid=(l+r)>>1;
int val=before.query(before.rt[x],1,n,1,mid);
if(itr.dir!=-1)val-=seg.query(seg.rt[itr.dir],1,n,1,mid);
if(val>=itr.k){
ret=mid;
r=mid-1;
}else l=mid+1;
}
ans[itr.idx]=(ll)n*n*(itr.x-1)+(ll)n*(itr.LCA-1)+(ret-1);
}
for(auto to:G[x])seg.rt[x]=seg.merge(seg.rt[x],seg.rt[to],1,n);
}
int main(){
fastio;
cin>>n>>q;
tr.init(n);
G=vector<vector<int>>(n+1);
Q1=Q2=vector<vector<Query>>(n+1);
rep(i,1,n){
cin>>p[i];
if(p[i]==0)st=i;
else G[p[i]].push_back(i);
}
dfs0(st);
rep(i,1,q){
ll k;cin>>k;
// k--;
int x=(k+n-1)/n;
k=(k-1)%n+1;
Q1[x].push_back((Query){x,-1,-1,(int)k,i,-1});
}
dfs1(st);
rep(i,1,n){
before.modify(before.rt[i],1,n,i,1);
seg.modify(seg.rt[i],1,n,i,1);
}
dfs2(st);
rep(i,1,q)cout<<ans[i]<<endl;
return 0;
}