题目链接: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 ;
}
复杂操作查询的高级数据结构实现
本文介绍了解决特定序列操作问题的四种高级数据结构实现方法:线段树、树状数组、AVL树及SBT树。通过这些数据结构可以高效处理包括插入、加减、删除及查找等操作。
327

被折叠的 条评论
为什么被折叠?



