HNOI2004 宠物收养所 SBT

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1208

思路:本题可以用线段树或者是SBT树做,用线段树只要找出给定一个数的左极限点和右极限点,然后决策选择哪个点,再用线段树单点更新就可以。 用SBT其实思路和线段树类似。

线段树:

#include <stdio.h>
#include <string.h>
#include <map>
typedef long long LL ;
const int NN = 80010 ;
const int Mod = 1000000 ;
int op[NN] , P[NN] ;
int N ;
std::map<int,int> mp ;
int rev[NN] , cnt ;
int EE[ NN << 2 ] ;
LL inf ;

void update(int l , int r, int idx, int pos , int v ){
    if( l == r ){
        EE[idx] = v ;
        return ;
    }
    int mid = (l + r) >> 1 ;
    if( pos <= mid )    update(l , mid , idx<<1,pos , v ) ;
    else                update(mid+1, r , idx<<1|1 ,pos ,v) ;
    EE[idx] = EE[idx<<1] | EE[idx<<1|1] ;
}

int query_left(int l, int r, int idx, int pos ){
    if( EE[idx]==0 )  return -1 ;
    if(l == r ) return l ;
    int mid = (l + r) >> 1 ;
    if( pos <= mid )    return query_left(l , mid , idx<<1 , pos ) ;
    else{
        int n = query_left( mid+1, r ,idx<<1|1 , pos ) ;
        if( n != -1 )   return n ;
        return query_left( l , mid , idx<<1 , pos ) ;
    }
}

int query_right(int l ,int r, int idx, int pos ){
    if( 0 == EE[idx] )  return -1 ;
    if( l == r )    return l ;
    int mid = (l + r) >> 1 ;
    if( pos <= mid ){
        int n = query_right(l , mid , idx<<1 , pos ) ;
        if( n!=-1 ) return n ;
        return query_right( mid+1 , r ,idx<<1|1 , pos ) ;
    }
    else        return query_right( mid+1 ,r , idx<<1|1 , pos ) ;
}

void solve(){
    memset( EE , 0 ,sizeof(EE) ) ;
    int f , left , right ;
    LL ans = 0 ;

    for(int i=0;i<N;i++){
        if( EE[1] == 0  ){
            f = op[i] ;
            update(1 , cnt , 1 , mp[P[i]] , 1 ) ;
        }
        else{
            if( f == op[i] ){
                update(1 , cnt , 1 , mp[P[i]] , 1 ) ;
            }
            else{
                left = query_left(1 , cnt , 1 , mp[P[i]] ) ;
                right = query_right(1 , cnt , 1 , mp[P[i]] ) ;
                LL up , down ;
                if( left == -1 )    down = inf ;
                else            down = (LL)P[i] - (LL)rev[left] ;
                if( right == -1 )   up = inf ;
                else                up = (LL)rev[ right ] - (LL)P[i] ;

                if( up >= down ){
                    ans += down ;
                    update( 1 , cnt , 1 , left , 0 ) ;
                }
                else{
                    ans += up ;
                    update(1 , cnt , 1 , right , 0 ) ;
                }
                ans %= Mod ;
            }
        }
    }
    printf("%lld\n",ans);
}

int main(){
    inf = 1e15 ;
    while( scanf("%d",&N) == 1 ){
        mp.clear() ;
        for(int i=0;i<N;i++){
            scanf("%d %d",&op[i] ,&P[i]);
            mp[ P[i] ] = 1 ;
        }
        std::map<int,int>::iterator it = mp.begin() ;
        cnt = 0 ;
        for( ; it!=mp.end();++it){
            it->second = ++cnt ;
            rev[cnt] = it->first ;
        }
        solve() ;
    }
    return 0 ;
}

SBT

#include <stdio.h>
#include <string.h>
typedef long long LL ;
const int NN = 80010 ;
const int Mod = 1000000 ;

int left[NN] , right[NN] , size[NN] , key[NN] ;
int T , N , node ;
void left_rotate(int &T){
    int k = right[T] ;
    right[T] = left[k] ;
    left[k] = T ;
    size[k] = size[T] ;
    size[T] = size[left[T]] + size[right[T]] + 1 ;
    T = k ;
}
void right_rotate(int &T){
    int k = left[T] ;
    left[T] = right[k] ;
    right[k] = T;
    size[k] = size[T] ;
    size[T] = size[left[T]] + size[right[T]] + 1 ;
    T = k ;
}
void Maintain(int &T , int f){
    if( f == 1 ){
        if( size[right[right[T]]] > size[left[T]] )
            left_rotate( T );
        else if( size[left[right[T]]] > size[left[T]] )
            right_rotate(right[T]) , left_rotate(T) ;
        else
            return ;
    }
    else{
        if( size[left[left[T]]] > size[right[T]] )
            right_rotate( T );
        else if( size[right[left[T]]] > size[right[T]] )
            left_rotate( left[T] ) , right_rotate(T) ;
        else
            return ;
    }
    Maintain( left[T] , 0 ) ;
    Maintain( right[T] , 1 ) ;
    Maintain( T , 0 ) ;
    Maintain( T , 1 ) ;
}
void add(int &T , int v){
    T = ++node ;
    left[T] = right[T] = 0 ;
    size[T] = 1 ; key[T] = v ;
}
void insert(int &T , int v){
    if( T == 0 ){
        add( T , v ) ;
        return ;
    }
    size[T]++ ;
    if( v < key[T] )
        insert( left[T] , v ) ;
    else
        insert( right[T] , v ) ;
    Maintain( T,  v >= key[T] ) ;
}

int query_left(int &T, int v){
    if(T == 0)  return -1 ;
    if( v <= key[T] )
        return query_left( left[T] , v ) ;
    else{
        int n = query_left( right[T] , v ) ;
        if( n != -1 )   return n ;
        return key[T] ;
    }
}
int query_right(int &T, int v){
    if(T == 0)  return -1 ;
    if( v >= key[T] )   return query_right( right[T] , v ) ;
    else{
        int n = query_right( left[T] , v ) ;
        if( n!=-1 ) return n ;
        return key[T] ;
    }
}
void Erase(int & , int ) ;

void remove(int &T){
    if( left[T] && right[T] ){
        int tmp = right[T] ;
        while( left[tmp] )  tmp = left[tmp] ;
        key[T] = key[tmp] ;
        Erase( right[T] , key[T] ) ;
    }
    else{
        if( left[T] == 0 )
            T = right[T] ;
        else
            T = left[T] ;
    }
}

void Erase(int &T, int v ){
    if(T == 0)  return ;
    size[T]-- ;
    if(v==key[T]){
        remove(T) ;
        return ;
    }
    else if( v<key[T] )
        Erase(left[T] , v) ;
    else
        Erase(right[T] , v ) ;
}

int main(){
    int ff ,a , b ;
    LL ans  , inf = 1000000000000000LL ;
    while( scanf("%d",&N) == 1 ){
        ans = T = node = left[0] = right[0] = size[0] = 0 ;
        for(int i=0;i<N;i++){
            scanf("%d%d",&a,&b);
            if( T == 0 ){
                ff = a ;
                insert(T , b);
            }
            else{
                if(ff == a)
                    insert( T , b ) ;
                else{
                    int down = query_left( T , b ) ;
                    int up = query_right(T , b) ;
                    LL d1 ,d2 ;
                    d1 = down==-1?inf:b-down ;
                    d2 = up==-1?inf:up-b ;
                    if( d1 <= d2 ){
                        ans += d1 ;
                        Erase(T , down) ;
                    }
                    else{
                        ans += d2 ;
                        Erase(T , up) ;
                    }
                    ans %= Mod ;
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值