Count on a tree SPOJ - COT 主席树+LCA

You are given a tree with N nodes. The tree nodes are numbered from 1 to N. Each node has an integer weight.

We will ask you to perform the following operation:

  • u v k : ask for the kth minimum weight on the path from node u to node v

Input

In the first line there are two integers N and M. (N, M <= 100000)

In the second line there are N integers. The ith integer denotes the weight of the ith node.

In the next N-1 lines, each line contains two integers u v, which describes an edge (uv).

In the next M lines, each line contains three integers u v k, which means an operation asking for the kth minimum weight on the path from node u to node v.

Output

For each operation, print its result.

Example

Input:
8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
2 5 2
2 5 3
2 5 4
7 8 2 
Output:
2
8
9
105
7 
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<queue>
#include<vector>
#include<cstring>
#include<string>
#include<map>
using namespace std;
const int maxn = 100010;
const int maxnn= 2500010;
int sca[maxn],T[maxn],ls[maxnn],rs[maxnn],sum[maxnn];
int cnt,n;
vector<int> vec,edge[maxn];
int ST[maxn*2][20],STcod[maxn*2][20],TIME,in[maxn],out[maxn],fa[maxn];
void build_new(int x,int bef,int val);
int getind(int x)
{
    return lower_bound(vec.begin(),vec.end(),x)-vec.begin();
}
void init()
{
    n=cnt=TIME=0;
    vec.clear();
    memset(sum,0,sizeof(sum));
}

void dfs(int x,int pre,int deep) //欧拉序初始化ST表,同时递归建树
{
    build_new(x,pre,getind(sca[x]));//注意传入节点的值是离散化后的值

    fa[x]=pre;
    in[x]=++TIME;
    ST[TIME][0]=deep;
    STcod[TIME][0]=x;
    for(int i=0;i<edge[x].size();i++)
    {
        if(edge[x][i]==pre)
            continue;
        dfs(edge[x][i],x,deep+1);
        ST[++TIME][0]=deep;
        STcod[TIME][0]=x;
    }
    out[x]=TIME;
}

void get_ST(int n)//建立ST表
{
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            if(ST[i][j-1]<ST[i+(1<<(j-1))][j-1])
            {
                 ST[i][j]=ST[i][j-1];
                 STcod[i][j]=STcod[i][j-1];
            }
            else
            {
                ST[i][j]=ST[i+(1<<(j-1))][j-1];
                STcod[i][j]=STcod[i+(1<<(j-1))][j-1];
            }
        }
    }
}
int RMQ(int l,int r)//RMQ求LCA
{
    if(l>r) swap(l,r);
    int k=0;
    while((1<<(k+1))<=r-l+1) k++;
    if(ST[l][k]<ST[r-(1<<k)+1][k])
        return STcod[l][k];
    else
        return STcod[r-(1<<k)+1][k];
}

int build(int l,int r)//初始化建树
{
    int now=++cnt;
    if(l<r)
    {
        int mid=(l+r)/2;
        ls[now]=build(l,mid);
        rs[now]=build(mid+1,r);
    }
    return now;
}
void build_new(int x,int pre,int val)//对新插入的点建树
{
    T[x]=++cnt;
    int now=T[x],bef=T[pre];
    sum[now]=sum[bef]+1;
    int l=0,r=n;
    while(ls[bef]||rs[bef])
    {
        int mid=(l+r)/2;
        if(val<=mid)
        {
            ls[now]=++cnt,rs[now]=rs[bef];
            sum[ls[now]]=sum[ls[bef]]+1;
            now=ls[now],bef=ls[bef];
            r=mid;
        }
        else
        {
            ls[now]=ls[bef],rs[now]=++cnt;
            sum[rs[now]]=sum[rs[bef]]+1;
            now=rs[now],bef=rs[bef];
            l=mid+1;
        }
    }
}
int query(int a,int b,int c,int d,int l,int r,int k)//查询
{
    if(l==r)
        return l;
    int lm=sum[ls[a]]+sum[ls[b]]-sum[ls[c]]-sum[ls[d]];//两条树链相加后减去重合的部分
    int mid=(l+r)/2;
    if(k<=lm)
        return query(ls[a],ls[b],ls[c],ls[d],l,mid,k);
    else
        return query(rs[a],rs[b],rs[c],rs[d],mid+1,r,k-lm);
}
int main()
{
    int N,m;
    while(scanf("%d %d",&N,&m)!=-1)
    {
        init();
        for(int i=1; i<=N; i++)
        {
            scanf("%d",&sca[i]);
            vec.push_back(sca[i]);
        }
        for(int i=1;i<N;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            edge[a].push_back(b);
            edge[b].push_back(a);
        }
        sort(vec.begin(),vec.end());//离散化
        vec.erase(unique(vec.begin(),vec.end()),vec.end());
        n=vec.size();
        T[0]=build(0,n);
        dfs(1,0,1);
        get_ST(2*N);
        for(int i=0; i<m; i++)
        {
            int a,b,k;
            scanf("%d %d %d",&a,&b,&k);
            int c=RMQ(out[a],in[b]);
            int d=fa[c];
            printf("%d\n",vec[query(T[a],T[b],T[c],T[d],0,n,k)]);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值