【Luogu P2633 】Count on a tree

题目链接

题目描述

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

题解

主席树静态区间第K小的树上情况。
只要把序列上的变成按父子关系来就行,用DFS序。

求的时候因为是点权,记录每一个点到根的信息后,用两端的减去lca的和lca的父亲的即可。

代码如下:

//树上的前缀
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-48;
    return x*t;
}
const int N=1e5+100;
struct node{int ls;int rs;int sum;inline void clear(){ls=rs=sum=0;}};
struct edge{int to,next;}a[N<<1];
int head[N];int cnt=0,m;
node tr[N<<5];int cn=0;int rt[N];
int n;int w[N];int ID[N];int num;
inline void add(int x,int y){a[++cnt]=(edge){y,head[x]};head[x]=cnt;}
int deep[N],top[N],son[N];int fa[N];
inline int Get(int u){return (lower_bound(ID+1,ID+1+num,w[u])-ID);}
inline void update(int &u,int l,int r,int p)
{
    tr[++cn]=tr[u];u=cn;
    ++tr[u].sum;
    if(l==r) return;
    register int mid=l+r>>1;
    if(mid>=p) update(tr[u].ls,l,mid,p);
    else update(tr[u].rs,mid+1,r,p);
}
inline int dfs1(int u,int ff){
    deep[u]=deep[ff]+1;fa[u]=ff;register int si=1;register int maxn_si=-1;
    rt[u]=rt[ff];update(rt[u],1,num,Get(u));
    for(register int v,i=head[u];i;i=a[i].next){
        v=a[i].to;if(v==ff) continue;
        register int ss=dfs1(v,u);if(ss>maxn_si) son[u]=v,maxn_si=ss;si+=ss;
    }
    return si;
}
inline void dfs2(int u,int ff)
{
    top[u]=ff;if(!son[u]) return;dfs2(son[u],ff);
    for(register int v,i=head[u];i;i=a[i].next){v=a[i].to;if(v==son[u]||v==fa[u]) continue;dfs2(v,v);}
}
inline int LCA(int x,int y)
{
    while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]]) swap(x,y);x=fa[top[x]];}return deep[x]>deep[y]? y:x;
}
inline void build(int &u,int l,int r)
{
    if(!u) u=++cn;if(l==r) return;
    register int mid=l+r>>1;
    build(tr[u].ls,l,mid);build(tr[u].rs,mid+1,r);
}
inline int Query(int u1,int u2,int u3,int u4,int l,int r,int k)
{
    if(l==r) return l;
    register int sum=tr[tr[u1].ls].sum+tr[tr[u2].ls].sum-tr[tr[u3].ls].sum-tr[tr[u4].ls].sum;
    register int mid=l+r>>1;
    if(sum>=k) return Query(tr[u1].ls,tr[u2].ls,tr[u3].ls,tr[u4].ls,l,mid,k);
    else return Query(tr[u1].rs,tr[u2].rs,tr[u3].rs,tr[u4].rs,mid+1,r,k-sum);
}
int main()
{
    n=read();m=read();
    for(register int i=1;i<=n;++i) w[i]=ID[i]=read();
    sort(ID+1,ID+1+n);num=unique(ID+1,ID+1+n)-ID-1;
    build(rt[0],1,num);//建空树就可以直接不用管有的儿子不存在的情况了
    register int x,y,k;
    for(register int i=1;i<n;++i){x=read();y=read();add(x,y);add(y,x);}
    dfs1(1,0);
    dfs2(1,1);
    int lst=0;
    for(register int i=1;i<=m;++i){
        x=read();y=read();k=read();
        x^=lst;int lca=LCA(x,y);
        lst=ID[Query(rt[x],rt[y],rt[lca],rt[fa[lca]],1,num,k)];
        printf("%d\n",lst);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值