[POJ2104] 主席树模板题

第一次写主席树,比较畏惧,害怕又会因为一些小错误调试半天,就拿这玩意当模板题写了,然而并没有想象的困难。


抱怨几句

指针的写法要判NULL真的麻烦= =
随便写什么树一眼望过去全是

if(XXXX!=NULL)

关于题目

题目连接:POJ2104
题目大意:给你一串数字,每次询问区间[L,R]的第K大


分析与思路

感觉没有什么可以说的····
就是要注意建树的时候,后一个节点相对于前一个节点的树实际上只改动了一条链,或者说只有一条链与前一棵树不同。

如果元素要进左子树的话,右子树就会跟上个树这个区间的右子树是完全一样的,因此,可以直接将本树本节点的右子树指针接到上棵树当前节点的右儿子,这样即省时间,又省空间。
from Oyking cnblogs
细节看代码。


代码


Problem: 2104       User: Nafario
Memory: 49140K      Time: 2469MS
Language: C++       Result: Accepted


#include<algorithm> 
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;

int N , M ;
struct node{
    int siz ;
    node *ls , *rs ;
    node(){
        ls = rs = NULL ;
        siz = 0 ;
    }
    void update(){
        siz = 0 ;
        if(ls) siz += ls->siz ;
        if(rs) siz += rs->siz ;
    }

}w[200005 * 20] , *tw = w , *root[200005] ;

int ori[200005] , ns[200005] , arcns[200005] , so[200005] , siz ;

void read_uniq(){
    for( int i = 1 ; i <= N ; i ++ ){
        scanf( "%d" , &ori[i] ) ; 
        so[i] = ns[i] = ori[i] ;
    }
    sort ( so + 1 , so + N + 1 ) ;
    siz = unique( so + 1 , so + N + 1 ) - so - 1 ;
    for( int i = 1 ; i <= N ; i ++ ){
        ns[i] = lower_bound( so + 1 , so + siz + 1 , ns[i] ) - so  ;
        arcns[ ns[i] ] = ori[i] ;
    }
}

void Insert( node *&las , node *&nd , int lf , int rg , int val ) {
    nd = ++tw ;
    las = ( las == NULL  ?  &w[0] : las ) ;
    *nd = node();
    //printf( "nd = %d  lf = %d rg = %d val = %d\n" ,nd , lf , rg , val ) ;
    int mid = ( lf + rg ) >> 1 ;
    if( lf == rg ){
        *nd = *las ;
        nd->siz ++ ;
        return ;
    } else if( val <= mid ){
        Insert( las->ls , nd->ls , lf , mid , val ) ;
        nd->rs = las->rs ;
    } else {
        Insert( las->rs , nd->rs , mid + 1 , rg , val ) ;
        nd->ls = las->ls ;
    }
    nd->update() ;
}

int Query( node* &Lnd , node* &Rnd , int lf , int rg , int k){
    if( lf == rg )
        return  lf ;
    //printf("%d %d   lf = %d   rg = %d\n  k = %d" , Lnd , Rnd , lf , rg , k) ;
    Lnd = ( Lnd == NULL ? &w[0] : Lnd ) ;
    //Rnd = ( Rnd == NULL ? &w[0] : Rnd ) ;
    int Lt = 0 ;
    if( Rnd->ls ) Lt += Rnd->ls->siz ;
    if( Lnd->ls ) Lt -= Lnd->ls->siz ;
    //printf("Lt = %d\n" , Lt) ;
    int mid = ( lf + rg ) >> 1 ; 
    if( k > Lt )    return Query( Lnd->rs , Rnd->rs , mid + 1 , rg , k - Lt ) ;
    else            return Query( Lnd->ls , Rnd->ls , lf , mid , k ) ;
}

void solve(){
    int L , R , k ;
    w[0] = node() ;
    for( int i = 1 ; i <= N ; i ++ )
        Insert( root[i - 1] , root[i] , 1 , siz , ns[i] );
    for( int i = 1 ; i <= M ; i ++ ){
        scanf( "%d%d%d" , &L , &R , &k ) ;
        printf( "%d\n", arcns[ Query( root[ L - 1 ] , root[R] , 1 , siz , k ) ] );
    }
}

int main(){
    std::ios::sync_with_stdio(false);
    scanf( "%d%d" , &N , &M ) ;
    read_uniq() ;
    solve() ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值