Qtree3-主席树

题意

你被给定一棵带点权的n个点的有根数,点从1到n编号。
定义查询 query(x,k): 寻找以x为根的k大点的编号(从小到大排序第k个点)
假设没有两个相同的点权。
输入格式: 第一行为整数n,第二行为点权,接下来n-1行为树边,接下来一行为整数m,下面m行为两个整数x,k,代表query(x,k)
输出格式: m行,输出每次查询的结果。


数据范围

n 10 5       0 l i 10 9       li为标号为i的节点的点权


题解

主席树裸题


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define mid ((L+R)>>1)
using namespace std;
const int N=1e5+10;

struct P{int v,pos;}t[N];
bool cmp(const P&x,const P&y){return x.v<y.v;}
int n,q;

int vl[N],rvl[N],in[N],ot[N],df[N],dfn,cnt;
int rt[N],sz[N*33],ch[N*33][2];
int head[N],to[N<<1],nxt[N<<1],tot;

inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline void dfs(int x,int fa)
{
    in[x]=++dfn;df[dfn]=x;
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(j==fa) continue;
        dfs(j,x);
    }
    ot[x]=dfn;
}

inline void build(int pre,int &now,int L,int R,int pos)
{
    if(!now) now=++cnt;
    sz[now]=sz[pre]+1;
    if(!(L^R))return;
    if(pos<=mid){
       ch[now][1]=ch[pre][1];
       build(ch[pre][0],ch[now][0],L,mid,pos);
    }else{
       ch[now][0]=ch[pre][0];
       build(ch[pre][1],ch[now][1],mid+1,R,pos);
    }
}

inline int query(int ql,int qr,int L,int R,int k)
{
    if(!(L^R)) return L;
    int sum=sz[ch[qr][0]]-sz[ch[ql][0]];
    if(sum>=k) return query(ch[ql][0],ch[qr][0],L,mid,k);
    else return query(ch[ql][1],ch[qr][1],mid+1,R,k-sum);
}

int main(){
    int i,ix,iy,iz;
    n=rd();
    for(i=1;i<=n;++i) t[i].v=rd(),t[i].pos=i;
    for(i=1;i<n;++i){ix=rd();iy=rd();lk(ix,iy);lk(iy,ix);}
    dfs(1,0);
    sort(t+1,t+n+1,cmp);
    for(i=1;i<=n;++i) rvl[i]=t[i].pos,vl[t[i].pos]=i;
    for(i=1;i<=n;++i){build(rt[i],rt[i+1],1,n,vl[df[i]]);}
    q=rd();
    while(q--){ix=rd();iy=rd();printf("%d\n",rvl[query(rt[in[ix]],rt[ot[ix]+1],1,n,iy)]);}
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/80725679