P2633 Count on a tree(树上主席树)

题解:
主席树是一个类似前缀和的数据结构,具有和前缀和类似的区间加减及差分等性质。

在求解线性区间的第k小数时,我们需要将该区间内的所有数值离散化后扔到一颗主席树中,并在这颗主席树上左右递归,来找到第k小数。

树上类似:用树上每个节点维护它到根的路径上的权值线段树,每个节点用它的父节点更新,dfs建树,套个差分就好了: t [ u ] + t [ v ] − t [ l c a ] − t [ l c a f a ] t[u] + t[v] - t[lca] - t[lca_fa] t[u]+t[v]t[lca]t[lcafa]


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+50;
struct node{ int v,nxt; }edge[MAXN<<1];
int head[MAXN],a[MAXN],b[MAXN],tot,ans,sz,cnt;
int dep[MAXN],dp[MAXN][20],rt[MAXN];
struct Node{ int l,r,sum; }t[MAXN*32];
inline void add(int u,int v){
    edge[++tot].v=v; edge[tot].nxt=head[u]; head[u]=tot;
    edge[++tot].v=u; edge[tot].nxt=head[v]; head[v]=tot;
}
void update(int l,int r,int &x,int y,int k){
    x=++cnt; t[x]=t[y]; t[x].sum++;
    if(l==r) return;
    int mid=l+r>>1;
    if(k<=mid) update(l,mid,t[x].l,t[y].l,k);
    else update(mid+1,r,t[x].r,t[y].r,k);
}
int Query(int l,int r,int u,int v,int fa,int pfa,int k){
    if(l==r) return l;
    int mid=l+r>>1;
    int sum=t[t[u].l].sum+t[t[v].l].sum-t[t[fa].l].sum-t[t[pfa].l].sum;
    if(sum>=k) return Query(l,mid,t[u].l,t[v].l,t[fa].l,t[pfa].l,k);
    else return Query(mid+1,r,t[u].r,t[v].r,t[fa].r,t[pfa].r,k-sum);
}
void dfs(int u,int fa){
    dep[u]=dep[fa]+1; dp[u][0]=fa;
    update(1,sz,rt[u],rt[fa],a[u]);
    for(int i=1;i<20;i++) dp[u][i]=dp[dp[u][i-1]][i-1];
    for(int i=head[u];i;i=edge[i].nxt){
        int v = edge[i].v;
        if(v==fa) continue;
        dfs(v,u);
    }
}
inline int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=19;i>=0;i--)
        if(dep[dp[x][i]]>=dep[y])
            x=dp[x][i];
    if(x==y) return x;
    for(int i=19;i>=0;i--)
        if(dp[x][i]!=dp[y][i])
            x=dp[x][i],y=dp[y][i];
    return dp[x][0];
}
int main(){
    //freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
    int n,Q; scanf("%d%d",&n,&Q);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1); sz=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+sz+1,a[i])-b;
    for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),add(u,v);
    dfs(1,0);
    while(Q--){
        int u,v,k; scanf("%d%d%d",&u,&v,&k);
        u^=ans;
        int fa=lca(u,v);
        ans = b[Query(1,sz,rt[u],rt[v],rt[fa],rt[dp[fa][0]],k)];
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值