码迷,mamicode.com
首页 > 其他好文 > 详细

主席树——树链上第k大spoj COT

时间:2019-04-24 15:58:13      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:次数   ++   div   const   last   线段   --   uil   roo   

首先要求第k大就想到用主席树来处理

但是不能直接用树链剖分的dfs序来维护,因为一条链对应的dfs下标可能是断开的几段,无法用权值线段树来维护

那么久维护每个点到根节点的全值线段树,结点u的权值线段树记录了其到根节点路径上数值的出现次数

主席树相当于维护了一个前缀和,由树上前缀和可以分析出u->v路径上对应的那棵权值线段树应该是

  T[u]+T[v]-T[lca]-T[fa[lca]]

所以只要在dfs过程中,结点u依赖fa[u]进行更新主席树即可

那么问题解变成了每个结点u上维护到root的权值线段树,然后每次询问求lca(u,v),再按照上述公式去主席树上查询第k大

求lca可以用树剖,也可以倍增。。

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
const int N = 1e5+100;
struct Node{int lc,rc,sum;}T[N*20];
int fa[2*N][30], dep[2*N], vis[N];
int a[N], b[N], tot, cnt, head[N], len;
struct node{int to, next;} p[2*N];
void init(){
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    cnt=0;
    return ;
}
void add(int u,int v){
    p[cnt].to=v,p[cnt].next=head[u];head[u]=cnt++;
    p[cnt].to=u,p[cnt].next=head[v];head[v]=cnt++;
    return ;
}
int size,rt[N];
int build(int l,int r){
    int now=++size;
    T[now].lc=T[now].rc=T[now].sum=0;
    if(l==r)return now;
    int mid=l+r>>1;
    T[now].lc=build(l,mid);
    T[now].rc=build(mid+1,r); 
    return now;
}

int update(int l,int r,int last,int pos){//更新到pos点 
    int now=++size;
     T[now]=T[last];T[now].sum++;
     if(l==r)return now;
     int mid=l+r>>1;
     if(pos<=mid)T[now].lc=update(l,mid,T[last].lc,pos);
     else T[now].rc=update(mid+1,r,T[last].rc,pos);
     return now;
}
int query(int e1,int e2,int s1,int s2,int l,int r,int k){
    if(l==r)return l;
    int mid=l+r>>1;
    int sum=T[T[e1].lc].sum+T[T[e2].lc].sum-T[T[s1].lc].sum-T[T[s2].lc].sum;
    if(k<=sum)return query(T[e1].lc,T[e2].lc,T[s1].lc,T[s2].lc,l,mid,k);
    else return query(T[e1].rc,T[e2].rc,T[s1].rc,T[s2].rc,mid+1,r,k-sum);
}

void dfs(int u,int d,int f,int root){
    vis[u]=1,dep[u]=d,fa[u][0]=f;
    rt[u]=update(1,len,root,a[u]);
    for(int i=head[u];i!=-1;i=p[i].next){
        int v=p[i].to;
        if(vis[v]) continue;
        dfs(v,d+1,u,rt[u]);
    }
}

int f[maxn],son[maxn],d[maxn],siz[maxn];
void dfs1(int x,int pre,int deep){
    f[x]=pre;siz[x]=1;d[x]=deep;
    for(int i=head[x];i!=-1;i=p[i].next){
        int y=p[i].to;
        if(y==pre)continue;
        dfs1(y,x,deep+1);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]])son[x]=y;
    }
}
int id[maxn],rk[maxn],idx,top[maxn];
void dfs2(int x,int tp){
    top[x]=tp;id[x]=++idx;rk[idx]=x;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i!=-1;i=p[i].next){
        int y=p[i].to;
        if(y!=son[x] && y!=f[x])dfs2(y,y);
    }
}
int Query(int x,int y){
    while(top[x]!=top[y]){
        if(d[top[x]]<d[top[y]])swap(x,y);
        x=f[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    return x;
}
 
int main(){
    int t, 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);
    len=unique(b+1,b+n+1)-(b+1);
    tot=0;
    rt[0]=build(1,len);
    for(int i=1; i<=n; i++)  a[i]=lower_bound(b+1,b+len+1,a[i])-(b);
    init();
    for(int i=0;i<n-1;i++){
        int x, y;
        scanf("%d %d", &x, &y);
        add(x,y);
    }
    dfs(1,1,0,rt[0]);dfs1(1,0,1),dfs2(1,1);
    int ans=0;
    while(q--){
        int l, r, x;
        scanf("%d %d %d", &l, &r, &x);l^=ans;
        int pos=Query(l,r);
        printf("%d\n",ans=b[query(rt[l],rt[r],rt[pos],rt[fa[pos][0]],1,len,x)]);
    }
    return 0;
}

 

主席树——树链上第k大spoj COT

标签:次数   ++   div   const   last   线段   --   uil   roo   

原文地址:https://www.cnblogs.com/zsben991126/p/10762819.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!