BZOJ 1803: Spoj1487 Query on a tree III 主席树题解

Time Limit: 1 Sec Memory Limit: 64 MB
Submit: 595 Solved: 268

Description

You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels.

Input

The first line contains one integer n (1 <= n <= 10^5). The next line contains n integers li (0 <= li <= 109) which denotes the label of the i-th node. Each line of the following n - 1 lines contains two integers u, v. They denote there is an edge between node u and node v. Node 1 is the root of the tree. The next line contains one integer m (1 <= m <= 10^4) which denotes the number of the queries. Each line of the next m contains two integers x, k. (k <= the total node number in the subtree of x)

Output

For each query (x, k), output the index of the node whose label is the k-th largest in the subtree of the node x.

Sample Input

5

1 3 5 2 7

1 2

2 3

1 4

3 5

4

2 3

4 1

3 2

3 2

Sample Output

5

4

5

5


显然主席树是可以瞎搞的,只需要跑个dfs序,然后在dfs序上建主席树,然后就可以了嘛每次查一个点in和out中间的信息即可


#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<set>
const int MAXN=111111;
using namespace std;
struct Line{
    int to,nxt;
}line[MAXN*2+100];
struct Tree{
    int lson,rson,sum;
}tree[MAXN*40];
int head[MAXN],tail,sz;
map<int,int>mm;
void add_line(int from,int to){
    line[++tail].to=to;
    line[tail].nxt=head[from];
    head[from]=tail;
}
int timer,a[MAXN*2],root[MAXN*2],in[MAXN*2],out[MAXN*2],n,m,x,kk,from,to,dfn[MAXN*2];
vector<int>v;
int id(int x){return int(lower_bound(v.begin(),v.end(),x)-v.begin()+1);}
void dfs(int u,int fa){
    in[u]=++timer;
    dfn[in[u]]=u;
    for(int i=head[u];i;i=line[i].nxt){
        int v=line[i].to;
        if(v!=fa) dfs(v,u);
    }
    out[u]=timer;
}
void modify(int x,int &y,int l,int r,int loc){
    tree[++sz]=tree[x];y=sz;tree[y].sum++;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(loc<=mid) modify(tree[x].lson,tree[y].lson,l,mid,loc);
    else         modify(tree[x].rson,tree[y].rson,mid+1,r,loc);
}
int query(int x,int y,int k,int l,int r){
    if(l==r)return l;
    int mid=(l+r)>>1;
    int t=tree[tree[y].lson].sum-tree[tree[x].lson].sum;
    if(k<=t) return query(tree[x].lson,tree[y].lson,k,l,mid);
    else     return query(tree[x].rson,tree[y].rson,k-t,mid+1,r);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),v.push_back(a[i]),mm[a[i]]=i;
    for(int i=1;i<=n-1;i++){scanf("%d%d",&from,&to);add_line(from,to);add_line(to,from);}
    dfs(1,0);
    sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1;i<=n;i++) modify(root[i-1],root[i],1,n,id(a[dfn[i]]));
    scanf("%d",&m);
    while(m--){
        scanf("%d%d",&x,&kk);
        int vall=v[query(root[in[x]-1],root[out[x]],kk,1,n)-1];
        printf("%d\n",mm[vall]);
    }
    return 0;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值