主席树

主席树就是对原数列每个前缀[1…i]建立一棵线段树,线段树的每个节点存储某个前缀[1…i]中属于区间[L,R]的数字共有多少个。为了避免MLE,必须利用好相邻两个线段树之间的差别条件:相邻两个线段树最多只有log个节点信息不同。原因在于相邻两节点只增加了一个节点的值,所以只出现在线段树的某条路径中,最多log个节点
1.建立
首先建立一棵空线段树
2.更新
按照离散值找对应值。更新叶节点只会影响到根节点到叶节点的一条路径,因此只需要修改该路径上的信息域data。可以利用历史版本线段树,只更改包含该元素的线段树区间data
3.查询
由于主席树每个节点是结构相同的线段树,因此可以相减。只需要查询线段树二叉中的一支即可,与线段树查询思想很相似

poj2104
题意:给定n个数,求[l,r]区间k大数,无修改,多组询问
解法:直接套用主席树

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;

const int maxn = 100000+10;
const int maxn2 = 2000000+10;
int n,m;
int root[maxn],ls[maxn2],rs[maxn2],sz[maxn2],tot;
int num[maxn];
vector<int> li;

void Add(int l,int r,int x,int &y,int v){
    y=++tot;
    sz[y]=sz[x]+1;
    if(l==r) return;
    ls[y]=ls[x],rs[y]=rs[x];
    int m=(l+r)>>1;
    if(v<=m) Add(l,m,ls[x],ls[y],v);
    else Add(m+1,r,rs[x],rs[y],v);
}

int query(int l,int r,int x,int y,int k){
    if(l==r) return l;
    int m=(l+r)>>1;
    if(sz[ls[y]]-sz[ls[x]]>=k) return query(l,m,ls[x],ls[y],k);
    else return query(m+1,r,rs[x],rs[y],k-(sz[ls[y]]-sz[ls[x]]));
}

void init(){
    tot=0;
    memset(root,0,sizeof(root)); memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs)); memset(sz,0,sizeof(sz));
}

int main(){
    //freopen("a.txt","r",stdin);
    scanf("%d%d",&n,&m);
    li.clear();
    init();
    for(int i=1;i<=n;i++){
        scanf("%d",&num[i]);
        li.push_back(num[i]);
    }
    sort(li.begin(),li.end());
    li.erase(unique(li.begin(),li.end()),li.end());
    for(int i=1;i<=n;i++){
        int x=lower_bound(li.begin(),li.end(),num[i])-li.begin()+1;
        Add(1,li.size(),root[i-1],root[i],x);
    }
    for(int i=1;i<=m;i++){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",li[query(1,li.size(),root[l-1],root[r],k)-1]);
    }
    return 0;
}

bzoj2588
题意:点权数多组询问:<u,v>k
解法:按照dfs入戳给节点编号,然后建立每个节点到根节点的主席树,插入节点时,需要按照入戳大小从小到大插入,用父节点更新子节点。查找时需要用sz[u]+sz[v]-sz[lca(u,v)]-sz[fa[lca(u,v)][0]]计算

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;

const int maxn = 100000+10;
const int maxn2 = 2000000+10;
int n,m,num[maxn],last;
vector<int> li,g[maxn];
int root[maxn],sz[maxn2],ls[maxn2],rs[maxn2],tot;
int fa[maxn][20],d[maxn];
int id[maxn],in[maxn],inq;
void init(){
    li.clear(),tot=last=inq=0;
    memset(root,0,sizeof(root)); memset(sz,0,sizeof(sz)); memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs));
    memset(fa,0,sizeof(fa));
}

void dfs(int u,int f,int dep){
    inq++,id[u]=inq,in[inq]=u,d[u]=dep,fa[u][0]=f;
    for(int i=1;i<=17;i++)
        if((1<<i)<=d[u]) fa[u][i]=fa[fa[u][i-1]][i-1];
        else break;

    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==f) continue;
        dfs(v,u,dep+1);
    }
}

int lca(int u,int v){
    if(d[u]<d[v]) swap(u,v);
    int dd=d[u]-d[v];
    for(int i=0;i<=17;i++) if((1<<i)&dd) u=fa[u][i];
    for(int i=17;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    if(u==v) return u;
    return fa[u][0];
}

void Add(int l,int r,int x,int &y,int v,int num){
    y=++tot;
    sz[y]=sz[x]+num;
    if(l==r) return;
    ls[y]=ls[x],rs[y]=rs[x];
    int m=(l+r)>>1;
    if(v<=m) Add(l,m,ls[x],ls[y],v,num);
    else Add(m+1,r,rs[x],rs[y],v,num);
}

int query(int u,int v,int k){
    int f=lca(u,v),ff=fa[f][0];
    int l=1,r=li.size();
    int a=root[id[u]],b=root[id[v]],c=root[id[f]],d=root[id[ff]];
    while(l<r){
        int m=(l+r)>>1;
        int tans=sz[ls[a]]+sz[ls[b]]-sz[ls[c]]-sz[ls[d]];
        if(tans>=k) r=m,a=ls[a],b=ls[b],c=ls[c],d=ls[d];
        else l=m+1,a=rs[a],b=rs[b],c=rs[c],d=rs[d],k-=tans;
    }
    return li[l-1];
}

int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        for(int i=1;i<=n;i++){
            g[i].clear();
            scanf("%d",&num[i]);
            li.push_back(num[i]);
        }
        sort(li.begin(),li.end());
        li.erase(unique(li.begin(),li.end()),li.end());
        for(int i=1;i<n;i++){
            int u,v; scanf("%d%d",&u,&v);
            g[u].push_back(v),g[v].push_back(u);
        }
        dfs(1,0,0);
        for(int i=1;i<=n;i++){
            int u=in[i];
            int x=lower_bound(li.begin(),li.end(),num[u])-li.begin()+1;
            Add(1,li.size(),root[id[fa[u][0]]],root[i],x,1);
        }
        for(int i=1;i<=m;i++){
            int u,v,k;
            scanf("%d%d%d",&u,&v,&k); u^=last;
            last=query(u,v,k);
            printf("%d",last);
            if(i!=m) printf("\n");//without it =>PE
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值