【主席树+LCA 树上路径第K大值模板题】HYSBZ - 2588 || Spoj 10628. Count on a tree

K - Spoj 10628. Count on a tree  HYSBZ - 2588 

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

Input

第一行两个整数N,M。

第二行有N个整数,其中第i个整数表示点i的权值。

后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

最后M行每行两个整数(u,v,k),表示一组询问。

Output

M行,表示每个询问的答案。最后一个询问不输出换行符

Sample 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 0 5 2 10 5 3 11 5 4 110 8 2

Sample Output

2 8 9 105 7

Hint

HINT:

N,M<=100000

暴力自重。。。

标准的树上路径第k大模板题

路径保存,根据父亲建树

root【L】+root【R】-root【lca(L,R)】-root【fa【lca(L,R) 】】;

注意!!!题上给的边是无向边

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
int T[maxn],L[maxn*44],R[maxn*44],sum[maxn*44];
int a[maxn],b[maxn],f[maxn][22],dep[maxn],e,len,tot;
int to[maxn*2],head[maxn],nxt[maxn*2];

void add(int u,int v)
{
    e++;
    to[e]=v;
    nxt[e]=head[u];
    head[u]=e;
}

void build(int &rt,int l,int r)
{
    rt=++tot;
    sum[rt]=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(L[rt],l,mid);
    build(R[rt],mid+1,r);
}

void update(int &rt,int pre,int l,int r,int x)
{
    rt=++tot;
    L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+1;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(x<=mid) update(L[rt],L[pre],l,mid,x);
    else update(R[rt],R[pre],mid+1,r,x);
}

int query(int gen_fa,int gen,int ql,int qr,int l,int r,int k)
{
    if(l==r) return l;
    int res=sum[L[ql]]+sum[L[qr]]-sum[L[gen_fa]]-sum[L[gen]];
    int mid=(l+r)/2;
    if(res>=k) return query(L[gen_fa],L[gen],L[ql],L[qr],l,mid,k);
    else return query(R[gen_fa],R[gen],R[ql],R[qr],mid+1,r,k-res);
}

void dfs(int v,int fa)
{

    f[v][0]=fa;
    dep[v]=dep[fa]+1;
    update(T[v],T[fa],1,len,lower_bound(b+1,b+len+1,a[v])-b);

    for(int i=1;(1<<i)<=dep[v];i++)
    {
        f[v][i]=f[f[v][i-1]][i-1];
    }

    for(int i=head[v];i!=-1;i=nxt[i])
    {
        if(to[i]==fa) continue;
        dfs(to[i],v);
    }
}

int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;(1<<i)<=d;i++)
    {
        if((1<<i)&d) u=f[u][i];
    }
    if(u==v) return u;
    for(int i=20;i>=0;i--)
    {
        if(f[u][i]!=f[v][i])
        {
            u=f[u][i];
            v=f[v][i];
        }
    }
    return f[u][0];
}

int main()
{
    memset(head,-1,sizeof head);
    e=tot=0;
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    len=unique(b+1,b+n+1)-b-1;
    build(T[0],1,len);
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs(1,0);
    int ans=0;

    while(m--)
    {
        int ql,qr,k;
        scanf("%d%d%d",&ql,&qr,&k);
        ql=ql^ans;
        int gen=lca(ql,qr);
        ans=b[query(T[f[gen][0]],T[gen],T[ql],T[qr],1,len,k)];
        if(m!=0) printf("%d\n",ans);
        else printf("%d",ans);
    }

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值