PT07J - Query on a tree III
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 <= 105). 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 <= 104) 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.
Example
Input: 5 1 3 5 2 7 1 2 2 3 1 4 3 5 4 2 3 4 1 3 2 3 2 Output: 5 4 5 5
【分析】给你一棵树以及每个节点的权值,求某个节点以及往下子孙中第K大的节点是哪个。
先DFS给每个节点编号,然后对于每个节点记录他的开始编号和结束编号,然后就是划分树模板了。
#include <iostream> #include <cstring> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <time.h> #include <string> #include <map> #include <stack> #include <vector> #include <set> #include <queue> #define met(a,b) memset(a,b,sizeof a) #define pb push_back #define lson(x) ((x<<1)) #define rson(x) ((x<<1)+1) using namespace std; typedef long long ll; const int N=1e5+5; const int M=1e6+10; int le[N],re[N],lab[N],sorted[N]; int n,m,k,num=0; map<int,int>mp; struct Edge { int to,next; } edg[N*2]; int head[N],tot; void init() { tot = 0; memset(head,-1,sizeof(head)); } void addedge(int u,int v) { edg[tot].to = v; edg[tot].next = head[u]; head[u] = tot++; } void dfs(int u,int fa) { le[u]=++num; sorted[num]=lab[u]; for(int i=head[u]; i!=-1; i=edg[i].next) { int v=edg[i].to; if(v!=fa) { dfs(v,u); } } re[u]=num; } int tree[20][N]; int toleft[20][N]; void build(int l,int r,int dep) { if(l==r)return; int mid=(l+r)>>1; int same=mid-l+1; for(int i=l; i<=r; i++) if(tree[dep][i]<sorted[mid]) same--; int lpos=l; int rpos=mid+1; for(int i=l; i<=r; i++) { if(tree[dep][i]<sorted[mid]) { //去左边 tree[dep+1][lpos++]=tree[dep][i]; } else if(tree[dep][i]==sorted[mid]&&same>0) { //去左边 tree[dep+1][lpos++]=tree[dep][i]; same--; } else //去右边 tree[dep+1][rpos++]=tree[dep][i]; toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数 } build(l,mid,dep+1);//递归建树 build(mid+1,r,dep+1); } void initBuild() { for(int i=0; i<20; i++)tree[i][0]=toleft[i][0]=0; for(int i=1; i<=n; i++) { tree[0][i]=sorted[i]; } sort(sorted+1,sorted+n+1); build(1,n,0); } int query(int L,int R,int l,int r,int dep,int k) { if(l==r)return tree[dep][l]; int mid=(L+R)>>1; int cnt=toleft[dep][r]-toleft[dep][l-1]; if(cnt>=k) { //L+查询区间前去左边的数的个数 int newl=L+toleft[dep][l-1]-toleft[dep][L-1]; //左端点+查询区间会分入左边的数的个数 int newr=newl+cnt-1; return query(L,mid,newl,newr,dep+1,k);//注意 } else { //r+区间后分入左边的数的个数 int newr=r+toleft[dep][R]-toleft[dep][r]; //右端点减去区间分入右边的数的个数 int newl=newr-(r-l-cnt); return query(mid+1,R,newl,newr,dep+1,k-cnt);//注意 } } int main() { int u,v,x; init(); scanf("%d",&n); for(int i=1; i<=n; i++){ scanf("%d",&lab[i]); mp[lab[i]]=i; } for(int i=1; i<n; i++) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } dfs(1,0); initBuild(); scanf("%d",&m); while(m--){ scanf("%d%d",&x,&k); u=le[x];v=re[x]; int ans=query(1,n,u,v,0,k); printf("%d\n",mp[ans]); } return 0; }