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

主席树|求区间第k小模板

时间:2020-01-22 21:37:27      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:poi   rank   false   第k小   pac   std   mes   子节点   否则   

主席树

学了主席树,用来求区间上的第k小
写一下自己整理后的模板

求区间第k小

#include<bits/stdc++.h>
using namespace std;

//求区间第k小 

const int maxn = 500010;
struct node{
    int v,lc,rc;
}T[maxn * 21];
int n,m;
int root[maxn];
int e; 

void insert(int pre,int cur,int pos,int l,int r){
    if(l == r){
        T[cur].v = T[pre].v + 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <=mid ){
        T[cur].lc = ++e; //新建左子结点 
        T[cur].rc = T[pre].rc; //保留右子节点 
        insert(T[pre].lc,T[cur].lc,pos,l,mid); //insert左 
    }else{
        T[cur].rc = ++e; //新建右子节点 
        T[cur].lc = T[pre].lc; //保留左子结点 
        insert(T[pre].rc,T[cur].rc,pos,mid+1,r); //insert右 
    }
    T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
}


int query(int ql,int qr,int l,int r,int k){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sum = T[T[qr].lc].v - T[T[ql].lc].v;
    if(sum >= k) return query(T[ql].lc,T[qr].lc,l,mid,k);
    else return query(T[ql].rc,T[qr].rc,mid+1,r,k-sum);  //这个ql.rc 和 qr.lc什么时候用老是搞不清楚 
} 


int main(){
    int x,y,d,k;
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>d;
        root[i] = ++e;
        insert(root[i-1],root[i],d,1,n);
    }
    while(m--){
        cin>>x>>y>>k;
        cout<<query(root[x-1],root[y],1,n,k)<<endl;
    }
    return 0;
} 

SPOJ 3946主席树 + 离散化

当数据比较大时(比如这道题的1e9那么大),就需要先离散化了。

#include<bits/stdc++.h>
using namespace std;
//sp3946

const int maxn = 500010;
struct node{
    int v,lc,rc;
}T[maxn * 21];
int n,m;
int root[maxn];
int ranks[maxn];
int e; 

struct A{
    int x,idx;
    bool operator < (const A &temp)const{
        return x < temp.x;
    }
}a[maxn];

void insert(int pre,int cur,int pos,int l,int r){
    if(l == r){
        T[cur].v = T[pre].v + 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <=mid ){
        T[cur].lc = ++e; //新建左子结点 
        T[cur].rc = T[pre].rc; //保留右子节点 
        insert(T[pre].lc,T[cur].lc,pos,l,mid); //insert左 
    }else{
        T[cur].rc = ++e; //新建右子节点 
        T[cur].lc = T[pre].lc; //保留左子结点 
        insert(T[pre].rc,T[cur].rc,pos,mid+1,r); //insert右 
    }
    T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
}

int query(int ql,int qr,int l,int r,int k){
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sum = T[T[qr].lc].v - T[T[ql].lc].v;
    if(sum >= k) return query(T[ql].lc,T[qr].lc,l,mid,k);
    else return query(T[ql].rc,T[qr].rc,mid+1,r,k-sum);  //这个ql.rc 和 qr.lc什么时候用老是搞不清楚 
} 

int main(){
    int x,y,d,k;
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i].x;
        a[i].idx = i;
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++) ranks[a[i].idx] = i;
    for(int i=1;i<=n;i++){
        root[i] = ++e;
        insert(root[i-1],root[i],ranks[i],1,n);
    }
    while(m--){
        cin>>x>>y>>k;
        cout<<query(root[x-1],root[y],1,n,k)<<endl;
    }
    return 0;
} 

poi2014 出现次数次数>(r-l+1)/2

因为这个数 在区间(l,r)上出现的次数最多为1次或者0次
所以用主席树维护每个数出现的次数,然后query查询时 左子树出现次数>goal就往左走、右子树出现次数>goal就往右走,否则return 0,最后直到叶节点(l==r)就会找到目标数

#include<bits/stdc++.h>
using namespace std;
//poi2014

const int maxn = 500001;
struct node{
    int v,lc,rc;
}T[maxn * 21];
int n,m;
int root[maxn];
int e; 

void insert(int pre,int cur,int pos,int l,int r){
    if(l == r){
        T[cur].v = T[pre].v + 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <=mid ){
        T[cur].lc = ++e; //新建左子结点 
        T[cur].rc = T[pre].rc; //保留右子节点 
        insert(T[pre].lc,T[cur].lc,pos,l,mid); //insert左 
    }else{
        T[cur].rc = ++e; //新建右子节点 
        T[cur].lc = T[pre].lc; //保留左子结点 
        insert(T[pre].rc,T[cur].rc,pos,mid+1,r); //insert右 
    }
    T[cur].v = T[T[cur].lc].v + T[T[cur].rc].v;
}

int goal;

//对于读入的区间,取长度后进行询问
//如果左子树大于mid的就继续往左走
//右子树大于mid的就继续往右走,否则return 0
int query(int ql,int qr,int l,int r){ //这里 T数组两棵树相减  为什么左子树相减  右子树相减 没搞懂 
    if(l == r) return l;
    int mid = (l + r) >> 1; 
    if(T[T[qr].lc].v - T[T[ql].lc].v > goal) return query(T[ql].lc,T[qr].lc,l,mid);
    else if(T[T[qr].rc].v - T[T[ql].rc].v > goal) return query(T[ql].rc,T[qr].rc,mid+1,r);
    return 0;  //没有就返回0 
} 

int main(){
    int x,y,d;
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>d;
        root[i] = ++e;
        insert(root[i-1],root[i],d,1,n);
    }
    while(m--){
        cin>>x>>y;
        goal = y-x+1>>1;
        cout<<query(root[x-1],root[y],1,n)<<endl;
    }
    return 0;
} 

主席树|求区间第k小模板

标签:poi   rank   false   第k小   pac   std   mes   子节点   否则   

原文地址:https://www.cnblogs.com/fisherss/p/12229528.html

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