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;
}
posted @ 2024-03-04 16:11  yoshinow2001  阅读(69)  评论(0编辑  收藏  举报