bzoj2588 count on a tree 主席树

在树上建主席树,每个点以它的父亲节点作为历史版本。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 200005
using namespace std;
struct E{
    int to,nxt;
}b[maxn<<1];
int fst[maxn],tot=1;
void build(int f,int t)
{
    b[++tot]=(E){t,fst[f]};fst[f]=tot;
    b[++tot]=(E){f,fst[t]};fst[t]=tot;
}
int deep[maxn];
int fa[maxn][20];
int w[maxn],L[maxn],len;
int lca(int u,int v)
{
    if(deep[u]>deep[v])
        swap(u,v);
    int t=deep[v]-deep[u];
    for(int i=0;i<=17;i++)
        if((t>>i)&1)    v=fa[v][i];
    if(u==v) return u;
    for(int i=17;i>=0;i--)
        if(fa[v][i]!=fa[u][i])
        {
            v=fa[v][i];
            u=fa[u][i];
        }
    return fa[u][0];
}
int Rt[maxn];
struct zxs{
    int l,r,cnt;
}tree[4000005];
int T=0;
void insert(int l,int r,int x,int &y,int v)
{
    y=++T;
    tree[y]=tree[x];
    tree[y].cnt++;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(v<=mid) insert(l,mid,tree[x].l,tree[y].l,v);
    else     insert(mid+1,r,tree[x].r,tree[y].r,v);
}
int ask(int x,int y,int f,int k)
{
    x=Rt[x];y=Rt[y];
    int z=fa[f][0];f=Rt[f];z=Rt[z];
    int l=1,r=len;
    while(l<r)
    {
        int p=tree[tree[y].l].cnt+tree[tree[x].l].cnt;
        p-=tree[tree[f].l].cnt+tree[tree[z].l].cnt;
        int mid=(l+r)>>1;
        if(p>=k)
        {
            r=mid;
            x=tree[x].l;
            y=tree[y].l;
            f=tree[f].l;
            z=tree[z].l;
        }
        else
        {
            l=mid+1;
            x=tree[x].r;
            y=tree[y].r;
            f=tree[f].r;
            z=tree[z].r;
            k-=p;
        }
    }
    return L[l];
}
int n,m;
void dfs(int x)
{
    for(int i=1;i<=17;i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    int j=lower_bound(L+1,L+len+1,w[x])-L;
    insert(1,len,Rt[fa[x][0]],Rt[x],j);
    for(int i=fst[x];i;i=b[i].nxt)
    {
        int v=b[i].to;
        if(!deep[v])
        {
            deep[v]=deep[x]+1;
            fa[v][0]=x;
            dfs(v);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        L[i]=w[i];
    }
    sort(L+1,L+n+1);
    len=unique(L+1,L+n+1)-L-1;
    int x,y,k;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        build(x,y);
    }
    deep[1]=1;
    dfs(1);
    int lst=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&k);
        x^=lst;
        lst=ask(x,y,lca(x,y),k);
        printf("%d",lst);
        if(i!=m) puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值