题意:给出一棵树,树上每个节点都有权值,有m个询问,求u到v的路径上第k大的数。
思路:用主席树为树上每个节点建立一棵线段树,和区间第k大的求法差不多,由于是在树上,只是添加的顺序有一些问题。每个节点对应的线段树表示从当前节点到根节点的路径中的数据,根据这个,如果我们找到了两个节点的lca,那么我们可以发现,这两条路径其实覆盖了它们公共祖先到根节点的路径,求第k大的时候只要把这一部分减去就行了,不过要注意的是公共祖先这个节点有可能被算了两次,所以查询的时候的区间中有公共祖先,那么要将其减去(在这儿wa了两次。。。)。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
struct Node
{
int L,R;
int sum;
}node[maxn*20*2];
struct Edge
{
int v,next;
}edges[maxn<<1];
struct Data
{
int w,pos;
bool operator < (const Data &a)const
{
return w<a.w;
}
}num[maxn];
struct Q
{
int v,pos,k;
};
vector<Q>querys[maxn];
int node_cnt,n,q,nEdge;
int ans[maxn],d[maxn],head[maxn];
int root[maxn],rank[maxn],f[maxn];
int parents[maxn];
bool vis[maxn];
void AddEdge(int u,int v)
{
nEdge++;
edges[nEdge].v=v;
edges[nEdge].next=head[u];
head[u]=nEdge;
}
void Init()
{
for(int i=1;i<=n;++i) querys[i].clear();
memset(head,0xff,sizeof(head));
memset(vis,0,sizeof(vis));
f[1]=0;d[1]=1;
node_cnt=0;nEdge=-1;
}
int Find(int x)
{
return x==parents[x]?x:parents[x]=Find(parents[x]);
}
int build(int l,int r)
{
int x=node_cnt++;
node[x].sum=0;
if(l==r) return x;
int m=(l+r)>>1;
node[x].L=build(l,m);
node[x].R=build(m+1,r);
return x;
}
int Update(int p,int l,int r,int rt)
{
int x=node_cnt++;
node[x]=node[rt];
node[x].sum++;
if(l==r) return x;
int m=(l+r)>>1;
if(p<=m)
node[x].L=Update(p,l,m,node[x].L);
else
node[x].R=Update(p,m+1,r,node[x].R);
return x;
}
int Query(int r1,int r2,int r3,int l,int r,int k,int s)
{
if(l==r) return l;
int m=(l+r)>>1;
int tmp=node[node[r1].L].sum+node[node[r2].L].sum-node[node[r3].L].sum*2-(s>=l&&s<=m);
if(tmp>=k)
return Query(node[r1].L,node[r2].L,node[r3].L,l,m,k,s);
else
return Query(node[r1].R,node[r2].R,node[r3].R,m+1,r,k-tmp,s);
}
void tarjan(int u,int fa)
{
parents[u]=u;
vis[u]=true;
root[u]=Update(rank[u],1,n,root[f[u]]);
int sz=querys[u].size();
Q tmp;
for(int i=0;i<sz;++i)
{
tmp=querys[u][i];
if(vis[tmp.v])
{
int anc=Find(tmp.v);
ans[tmp.pos]=Query(root[u],root[tmp.v],root[f[anc]],1,n,tmp.k,rank[anc]);
}
}
for(int h=head[u];h!=-1;h=edges[h].next)
{
int v=edges[h].v;
if(v==fa) continue;
d[v]=d[u]+1;f[v]=u;
tarjan(v,u);
parents[v]=u;
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d",&n,&q))
{
Init();
for(int i=1;i<=n;++i)
{
scanf("%d",&num[i].w);
num[i].pos=i;
}
sort(num+1,num+n+1);
for(int i=1;i<=n;++i)
rank[num[i].pos]=i;
int u,v,k;
for(int i=1;i<n;++i)
{
scanf("%d%d",&u,&v);
AddEdge(u,v);
AddEdge(v,u);
}
root[0]=build(1,n);
Q tmp;
for(int i=0;i<q;++i)
{
scanf("%d%d%d",&u,&v,&k);
tmp.v=v;tmp.k=k;tmp.pos=i;
querys[u].push_back(tmp);
tmp.v=u;
querys[v].push_back(tmp);
}
tarjan(1,-1);
for(int i=0;i<q;++i)
printf("%d\n",num[ans[i]].w);
}
return 0;
}