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