【BZOJ2588】Count on a tree(主席树+树上差分)

我们直接在dfs的时候顺便建主席树 维护一个节点到根路径上的节点出现情况

对于查询(u,v)

用u点的主席树+v点的主席树-lca(u,v)的主席树-lca(u,v)父节点的主席树,在这样产生的主席树上查找第k小的排名,最后输出它原来的点权。

lca我选择了树链剖分

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m,q,first[N],tot;
int a[N],b[N];
struct Edge
{
    int to,next;
}edge[2*N];
inline void addedge(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=first[x];
    first[x]=tot;
}
int rt[N<<5],lson[N<<5],rson[N<<5],sum[N<<5],cnt;
inline int update(int now,int l,int r,int x)
{
    int newnode=++cnt;
    lson[newnode]=lson[now];    rson[newnode]=rson[now];    sum[newnode]=sum[now]+1;
    if(l==r)    return newnode;
    int m=(l+r)>>1;
    if(x<=m)    lson[newnode]=update(lson[newnode],l,m,x);
    else rson[newnode]=update(rson[newnode],m+1,r,x);
    return newnode;
}
int father[N],depth[N],size[N],maxson[N];
void dfs1(int now,int fa)
{
    father[now]=fa;
    depth[now]=depth[fa]+1;
    size[now]=1;
    rt[now]=update(rt[father[now]],1,q,a[now]);
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(vis==fa) continue;
        dfs1(vis,now);
        size[now]+=size[vis];
        if(size[vis]>size[maxson[now]]) maxson[now]=vis;
    }
}
int top[N];
void dfs2(int now,int topf)
{
    top[now]=topf;
    if(maxson[now]) dfs2(maxson[now],topf);
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(vis==father[now]||vis==maxson[now])  continue;
        dfs2(vis,vis);
    }   
}
inline int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(depth[top[x]]>=depth[top[y]])    x=father[top[x]];
        else y=father[top[y]];
    }
    if(depth[x]>=depth[y])  return y; 
    else return x;
}
inline int query(int rt1,int rt2,int rt3,int rt4,int l,int r,int k)
{
    if(l==r)    return l;
    int m=(l+r)>>1;
    int tmp=sum[lson[rt1]]+sum[lson[rt2]]-sum[lson[rt3]]-sum[lson[rt4]];
    if(tmp>=k)  return query(lson[rt1],lson[rt2],lson[rt3],lson[rt4],l,m,k);
    else return query(rson[rt1],rson[rt2],rson[rt3],rson[rt4],m+1,r,k-tmp);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);cout.tie(NULL);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    q=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(b+1,b+1+q,a[i])-b;
    }
    for(int i=1;i<n;i++)
    {
        int x,y;
        cin>>x>>y;
        addedge(x,y);
        addedge(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    int ans=0;
    while(m--)
    {
        int x,y,z,LCA;
        cin>>x>>y>>z;
        x^=ans; LCA=lca(x,y);
        ans=b[query(rt[x],rt[y],rt[LCA],rt[father[LCA]],1,q,z)];
        cout<<ans<<endl;
    }
    return 0;
}

转载于:https://www.cnblogs.com/Patrickpwq/articles/9810106.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值