SPOJ1487 Query on a tree III

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

题目大意:给你一颗以1为根的树,每个点权值各不相同,每次查询以x为根的子树中权值第k大的节点标号。

之前看过,一眼看上去水题,发现操作不来就没管这道题。

今天刚好切一道树剖题用到dfs序这个东西,那么不就是在dfs序上求区间第k大吗?

树套树/主席树乱搞,但是卡空间,就用主席树了。

补:今天早上琢磨树的操作时,又想到可以用dsu水过去。。

#include<bits/stdc++.h>
using namespace std;
#define show_memory(x) cout<<sizeof(x)/(1024*1024.0)<<"\n";
const int Maxn=100005;
struct Edge{
	int cnt,h[Maxn],to[Maxn*2],next[Maxn*2];
	inline void add(int x,int y){
		next[++cnt]=h[x];to[cnt]=y;h[x]=cnt;
	}
}e;
#define to e.to[p]
int n,m,v[Maxn];
int cnt,p[Maxn],fir[Maxn],sec[Maxn];
map<int,int>fp;
void dfs(int x,int fa){
	p[fir[x]=++cnt]=x;
	for(int p=e.h[x];p;p=e.next[p])
		if(to^fa)dfs(to,x);
	sec[x]=cnt;
}
struct SegMent{
	struct tree{
		int ls,rs,siz;
	}t[Maxn*40];
	int tot,root[Maxn];
	inline void insert(int &x,int o,int l,int r,int val){
		if(r<val||l>val)return ;
		t[x=++tot]=t[o];
		++t[x].siz;
		if(l==r)return ;
		int mid=l+r>>1;
		insert(t[x].ls,t[o].ls,l,mid,val),insert(t[x].rs,t[o].rs,mid+1,r,val);
	}
	inline int query(int R,int L,int l,int r,int kth){
		if(l==r)return fp[l];
		int size=t[t[R].ls].siz-t[t[L].ls].siz;
		int mid=l+r>>1;
		if(size>=kth)return query(t[R].ls,t[L].ls,l,mid,kth);
		else return query(t[R].rs,t[L].rs,mid+1,r,kth-size);
	}
}seg;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&v[i]);
		fp[v[i]]=i;
	}
	for(int i=1;i<n;++i){
		int x,y;scanf("%d%d",&x,&y);
		e.add(x,y),e.add(y,x);
	}
	dfs(1,0);
	for(int i=1;i<=n;++i)seg.insert(seg.root[i],seg.root[i-1],0,1e9,v[p[i]]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
		int x,k;scanf("%d%d",&x,&k);
		printf("%d\n",seg.query(seg.root[sec[x]],seg.root[fir[x]-1],0,1e9,k));
	}
//	show_memory(seg);
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值