NOI2004 郁闷的出纳员

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

题意:给出N中操作,分别是I(往一列数中增加一个数)、A(将数列中的现有数全部加上一个数)、S(将数列中的所有数全部都减去一个数,如果某个数的值小于给定的MIN,这个数就会被彻底地删除)、F(询问数列中现有数中的第K大数)。

思路:应用高级数据结构解题,可以用线段树、树状数组、或者平衡树。

线段树:

#include <stdio.h>
#include <string.h>
#include <algorithm>
int N ,M ;
const int len = 17 ;
const int base = 1 << len ;
const int NN = 3 * base ;

int sum[ NN << 2 ]  ;
int ss[ NN << 2 ] ;

void down(int l , int r , int idx){
    if( ss[idx] ){
        ss[idx<<1] = ss[idx<<1|1] = 1 ;
        ss[idx] = 0 ;
        sum[idx<<1] = sum[idx<<1|1] = 0 ;
        return ;
    }
}

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

int set(int l ,int r, int a, int b , int idx){
    if( l==a && r==b ){
        int res = sum[idx] ;
        sum[idx] = 0 ; ss[idx] = 1 ;
        return res ;
    }
    int mid = (l + r) >> 1 ;
    down( l ,r , idx ) ;
    int ret = 0 ;
    if( b<=mid )    ret = set(l , mid , a ,b , idx<<1) ;
    else if( a > mid )  ret = set( mid+1 , r , a , b, idx<<1|1) ;
    else{
        ret = set(l , mid , a , mid , idx<<1) ;
        ret += set(mid+1 ,r , mid+1, b, idx<<1|1 ) ;
    }
    sum[idx] = sum[idx<<1] + sum[idx<<1|1] ;
    return ret ;
}

int query(int l ,int r , int idx, int cnt ){
    if( l == r ){
        return l ;
    }
    int mid = ( l + r ) >> 1 ;
    down( l ,r , idx) ;
    if( sum[idx<<1] >= cnt )    return query( l , mid , idx<<1 , cnt ) ;
    else                        return query( mid+1 , r , idx<<1|1 , cnt - sum[idx<<1] )  ;
}

int main(){
    char ch[3] ; int a ;
    while( scanf("%d %d",&N,&M) == 2 ){
        memset( sum , 0 ,sizeof(sum) );
        memset( ss , 0 ,sizeof(ss) )  ;

        int del = 0 , ans = 0 , tot = 0 ;
        for(int i=0;i<N;i++){
            scanf("%s%d",ch,&a);
            if( ch[0] == 'I' ){
                if( a < M ) continue ;
                a = a + base - del ; tot++ ;
                update(1 , NN , 1 , a , 1 ) ;
            }
            else if( ch[0] == 'S' ){
                /*
                这里还有一种思路:就是枚举将要被删除的员工的工资,如果某个工资
                的人数不为0就用log(n)的时间在线段树中将其删除。
                这里主要说明一下为什么可以直接枚举工资:这是因为每次工资的变动
                最多就只有1000,假设在本次S之前的del为d1 ,本次S之后的del为d2,则有
                d1 > d2 ;那么在本次S之前,0 -- Min-1-dl这个区间肯定已经被“清零”了,
                而本次S之后的目的就是将0 -- Min-1-d2“清零”;那么也就是:
                [ Min-1-d1 , Min-1-d2 ] , 这个区间最大也就是工资的最大变动值,也就是
                1000 ,而S最多也就是100,这样最多就是100000*log(n),是不会超时的。
                */
                del -= a ;
                int aa = set(1, NN , 1 , base+M-1-del , 1) ;
                ans += aa ; tot -= aa ;
            }
            else if( ch[0] == 'A' ){
                del += a ;
            }
            else{   // 'F'
                if( tot < a ){
                    printf("-1\n");
                }
                else
                    printf("%d\n", query(1 , NN , 1 , tot - a + 1 ) - base + del );
            }
        }
        printf("%d\n",ans );
    }
    return 0 ;
}


树状数组:

#include <stdio.h>
#include <string.h>

const int BB = 100000 ;
const int LL = BB * 3 ;
int N , M ;
int out , tot ,del ;
int sum[ LL ] ;

int lowbit( int n ){ return n&(n^(n-1)) ; }

void insert_into( int n ,int v){
    while( n < LL ){
        sum[n] += v ;
        n += lowbit(n) ;
    }
}

void insert(int a){
    if( a < M ) return ;
    insert_into( a - del + BB , 1 ) ;
    tot++ ;
}

void add(int a){
    del += a ;
}

void sub( int ori ,int now ){
    int s,  e ;
    s = M - 1 + BB - ori ;
    e = M - 1 + BB - now ;
    for(int i=s;i<=e;i++){
        if( sum[i] > 0 ){
            tot -= sum[i] ;
            out += sum[i] ;
            insert_into( i , -sum[i] ) ;
        }
    }
}

void subtract(int a){
    sub( del , del - a ) ;
    del -= a ;
}

int search(int n){
    int ans = 0 ;
    while( n >= 1 ){
        ans += sum[n] ;
        n -= lowbit(n) ;
    }
    return ans ;
}

int query( int n ){
    int low = 1 , high = LL - 1 , mid ;
    while( low < high ){
        mid = (low + high) >> 1 ;
        int cnt = search( mid ) ;
        if(  cnt >= n ) high = mid ;
        else            low = mid + 1 ;
    }
    return low + del - BB ;
}

void find( int a ){
    if( a > tot ){
        printf("-1\n");
        return ;
    }
    a = tot + 1 - a ;
    printf("%d\n",query(a) );
}
int main(){
    char ch[4] ; int a ;
    while( scanf("%d%d",&N,&M) == 2 ){
        memset( sum , 0 , sizeof(sum) ) ;
        out = tot = del = 0 ;
        for(int i=0;i<N;i++){
            scanf("%s %d",ch,&a);
            switch( ch[0] ){
                case 'I' :  insert(a);  break ;
                case 'A' :  add(a);     break ;
                case 'S' :  subtract(a);break ;
                case 'F' :  find(a);    break ;
            }
        }
        printf("%d\n",out);
    }
    return 0 ;
}

AVL树:

#include <stdio.h>
#include <string.h>
#include <map>

const int NN = 100010 ;
int N , M ;
int del , tot, ans ,ncnt ;
std::map<int,int> mp ;

struct Node{
    int height ;
    struct Node *left, *right ;
    int size ;
    int value ;
    int num ;
    void init( int _v ){
        value = _v ;
        height = 0 ; size = 1 ;
        num = 1 ;
        left = right = NULL ;
    }
}p[NN];
typedef Node* pnode ;
pnode T ;

int calc_hei( pnode T ){
    if( T==NULL )   return -1;
    else            return T->height ;
}
int calc_size( pnode T ){
    if( T==NULL )   return 0 ;
    else            return T->size ;
}
int calc_num( pnode T ){
    if( T==NULL )   return 0 ;
    else            return T->num ;
}
pnode RotateLeft( pnode T ){
    pnode k = T->left ;
    T->left = k->right ;
    k->right = T ;
    T->height = std::max( calc_hei(T->left) , calc_hei(T->right) ) + 1 ;
    T->size = calc_size( T->left ) + calc_size( T->right ) + calc_num(T) ;
    k->height = std::max( calc_hei(k->left) , calc_hei(k->right) ) + 1 ;
    k->size = calc_size( k->left ) + calc_size( k->right ) + calc_num(k);
    return k ;
}

pnode RotateRight( pnode T ){
    pnode k = T->right ;
    T->right = k->left ;
    k->left = T ;
    T->height = std::max( calc_hei(T->left) , calc_hei(T->right) ) + 1 ;
    T->size = calc_size( T->left ) + calc_size( T->right ) + calc_num(T) ;
    k->height = std::max( calc_hei(k->left) , calc_hei(k->right) ) + 1 ;
    k->size = calc_size( k->left ) + calc_size( k->right ) + calc_num(k);
    return k ;
}

pnode insert( pnode T , int a ){
    if( T==NULL ){
        T = &p[ncnt++] ;
        T->init( a ) ;
        return T ;
    }
    if( T->value == a ){
        T->size++ ; T->num++ ;
        return T ;
    }
    else if( a < T->value ){
        //左
        T->left = insert( T->left , a ) ;
        if( calc_hei(T->left) - calc_hei(T->right) == 2 ){
            if( a < T->left->value ){
                T = RotateLeft( T ) ;
            }
            else{
                T->left = RotateRight( T->left );
                T = RotateLeft( T ) ;
            }
        }
    }
    else{
        //右
        T->right = insert( T->right , a ) ;
        if( calc_hei(T->right) - calc_hei(T->left) == 2 ){
            if( a < T->right->value ){
                T->right = RotateLeft( T->right ) ;
                T = RotateRight( T ) ;
            }
            else
                T = RotateRight( T ) ;
        }
    }
    T->height = std::max( calc_hei(T->left) , calc_hei(T->right) ) + 1 ;
    T->size = calc_size( T->left ) + calc_size( T->right ) + calc_num( T ) ;
    return T ;
}

pnode move(pnode, int) ;

pnode out( pnode T ){
    if( T->left!=NULL && T->right!=NULL ){
        pnode tmp = T->right ;
        while( tmp->left != NULL ) tmp = tmp->left ;
        T->value = tmp->value ;
        T->num = tmp->num ;
        T->right = move( T->right , T->value ) ;
        T->size = calc_size( T->left ) + calc_size( T->right ) + calc_num(T) ;
        T->height = std::max( calc_hei( T->left ) , calc_hei( T->right ) ) + 1 ;
        return T ;
    }
    else{
        if( T->left == NULL ){
            T = T->right ;
            return T ;
        }
        else{
            T = T->left ;
            return T ;
        }
    }
}

pnode move( pnode T , int a ){
    if( T==NULL )   return NULL ;
    if( T->value == a ){
        T = out( T ) ;
        return T ;
    }
    if( a < T->value ){
        T->left = move( T->left , a ) ;
        if( calc_hei(T->right) - calc_hei(T->left) == 2 )
            if( T->right->left!=NULL && calc_hei(T->right->left) > calc_hei(T->right->right) ){
                T->right = RotateLeft( T->right ) ;
                T = RotateRight( T ) ;
            }
            else
                T = RotateRight( T ) ;
    }
    else{
        T->right = move( T->right , a ) ;
        if( calc_hei(T->left) - calc_hei(T->right) == 2 )
            if( T->left->right!=NULL && calc_hei(T->left->right) > calc_hei(T->left->left) ){
                T->left = RotateRight( T->left ) ;
                T = RotateLeft( T ) ;
            }
            else
                T = RotateLeft( T ) ;
    }
    if( T==NULL )   return NULL ;
    T->size = calc_size( T->left ) + calc_size( T->right ) + calc_num(T) ;
    T->height = std::max( calc_hei( T->left ) , calc_hei( T->right ) ) + 1 ;
    return T ;
}

void deal(int s,  int e ){
    for(int i=M-1-s;i<=M-1-e;++i){
        if( mp[i] == 0 )    continue ;
        tot -= mp[i] ; ans += mp[i] ; mp[i] = 0 ;
        T = move( T , i ) ;
    }
}

int search( pnode T  , int a ){
    int cc = T->num ;
    if( calc_size(T->left) >= a-cc && calc_size(T->left)<=a-1 ){
        return T->value ;
    }
    if( calc_size(T->left) < a-cc ){
         return search( T->right , a-calc_size(T->left)-cc  ) ;
    }
    else{
        return search( T->left ,a ) ;
    }
}

void output( int a ){
    if( a > tot ){
        printf("-1\n"); return ;
    }
    a = tot + 1 - a ;
    printf("%d\n",search(T , a) + del ) ;
}
int main(){
    char ch[4] ; int a ;
    T = NULL ;
    while( scanf("%d%d",&N,&M) == 2 ){
        int cas = 0 ;
        del = tot = ans = 0 ; mp.clear() ; ncnt = 0 ;
        for(int i=0;i<N;++i){
            scanf("%s%d",ch,&a);
            switch( ch[0] ){
                case 'I' :
                    if( a >= M ){
                        tot++ ;
                        a = a - del ;
                        mp[a]++ ;
                        T = insert(T , a) ;
                    }
                    break ;
                case 'A' :
                    del += a ;
                    break ;
                case 'S' :
                    deal( del ,del-a ) ;
                    del -= a ;
                    break ;
                case 'F' :
                    output( a ) ;
                    break ;
            }
        }
        printf("%d\n",ans);
    }
    return 0 ;
}

SBT树:

#include <stdio.h>
#include <string.h>
const int NN = 100010 ;
int left[NN] , right[NN] , size[NN] , key[NN] ;
int T , node , del , leave , N , M ;

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 add(int &T, int v){
    T = ++node ;
    left[T] = right[T] = 0;
    size[T] = 1 ;key[T] = v ;
}
void Maintain(int &T,int flag){
    if( flag == 0 ){
        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 ;
    }
    else{
        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 ;
    }
    Maintain( left[T] , 0 ) ;
    Maintain( right[T] , 1 ) ;
    Maintain( T , 0 ) ;
    Maintain( T , 1 ) ;
}
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 erase(int &T,int v){
    if(T == 0)  return 0 ;
    int n ;
    if( v < key[T] ){
        n = erase( left[T] , v )  ;
        size[T] -= n ;
    }
    else{
        n = size[ left[T] ] + 1 + erase( right[T] , v ) ;
        T = right[T] ;
    }
}
int query(int &T,int n){
    int k = size[ left[T] ] + 1 ;
    if( n == k )    return  key[T] ;
    if( n < k )     return  query( left[T]  , n ) ;
    else            return  query( right[T] , n - k ) ;
}

int main(){
    char ch[5] ; int a ;
    while( scanf("%d%d",&N,&M)==2 ){
        del = T = leave = node = size[0] = left[0] = right[0] = 0 ;
        for(int i=0;i<N;i++){
            scanf("%s%d",ch,&a);
            if( ch[0]=='I' ){
                if( a >= M )
                    insert( T ,a - del ) ;
            }
            else if( ch[0] == 'A' ){
                del += a;
            }
            else if( ch[0] == 'S' ){
                del -= a ;
                leave += erase( T , M-1-del ) ;
            }
            else{
                if( a > size[T] )
                    printf("-1\n");
                else
                    printf("%d\n",query(T,size[T]+1-a)+del );
            }
        }
        printf("%d\n",leave);
    }
    return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值