Spoj 10628. Count on a tree HYSBZ - 2588 (树链剖分+主席树)

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大值

思路:因为没有修改,所以直接剖分,并建主席树,v,u之间新数的个数:sum[u] + sum[v] - sum[lca(u , v)] - sum[fa[lca(u , v)]],不多说,代码不长,直接上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=100005;
struct Edge
{
    int a,b,pre;
}edge[2*N];
struct Node
{
    int l,r,sum;
}tree[30*N];
int dis[N],pre[N],fa[N],son[N],siz[N],top[N],wei[N],root[N],len[N],number[N],wei_num,edge_num,node_num,num;

void addedge(int a,int b)
{
    edge[++edge_num]={a,b,pre[a]};
    pre[a]=edge_num;
}

void addtree(int l,int r,int &node,int pre,int p)
{
    node=++node_num;
    tree[node]=tree[pre];
    tree[node].sum+=1;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid) addtree(l,mid,tree[node].l,tree[pre].l,p);
    else addtree(mid+1,r,tree[node].r,tree[pre].r,p);
}

int query(int ll,int rr,int u,int v,int l,int f,int k)
{
    if(ll==rr)return ll;
    int nn=tree[tree[u].l].sum+tree[tree[v].l].sum-tree[tree[l].l].sum-tree[tree[f].l].sum;
    int mid=(ll+rr)>>1;
    if(k<=nn)return query(ll,mid,tree[u].l,tree[v].l,tree[l].l,tree[f].l,k);
    else return query(mid+1,rr,tree[u].r,tree[v].r,tree[l].r,tree[f].r,k-nn);
}

void dfs1(int u,int f,int d)
{
    dis[u]=d;
    fa[u]=f;
    son[u]=0;
    siz[u]=1;
    int p=lower_bound(number+1,number+1+num,len[u])-number;
    addtree(1,num,root[u],root[f],p);
    for(int i=pre[u];i;i=edge[i].pre){
        int v=edge[i].b;
        if(v==f)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}

void dfs2(int u,int tp)
{
    top[u]=tp;
    wei[u]=++wei_num;
    if(son[u])dfs2(son[u],tp);
    for(int i=pre[u];i;i=edge[i].pre){
        int v=edge[i].b;
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

int yongth(int a,int b)
{
    for(;top[a]!=top[b];dis[top[a]]>dis[top[b]]?(a=fa[top[a]]):(b=fa[top[b]]));
    return dis[a]<dis[b]?a:b;
}

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&len[i]);
        number[i]=len[i];
    }

    int a,b;
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        addedge(a,b);
        addedge(b,a);
    }

    sort(number+1,number+1+n);
    num=unique(number+1,number+1+n)-number-1;
    dfs1(1,0,1);
    wei_num=0;
    dfs2(1,1);

    int last=0,u,v,k;
    while(q--){
        scanf("%d%d%d",&u,&v,&k);
        u^=last;
        int lca=yongth(u,v);
        last=number[query(1,num,root[u],root[v],root[lca],root[fa[lca]],k)];
        printf("%d",last);
        if(q!=0)printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值