hdu 4288 Coder (成都赛区 线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=4288

题意:

给出一个有序集合,3种操作。插入一个数,删除一个数,都保证序列有序。以及求和

其中求和是将下标%5==3的所有数求和;

 

题解: 线段树   + 离散化 + 离线处理

一开始也是想的 线段树 ,但是 这个和以前的 做过的 一个线段树 不同的 是 ,如果 我们 删除 一个 元素后  ,那么 他的 下标 将会 改变 ,比赛是 不知  如何下手 。。。。。

 

 

同样 是 用 5棵线段树   维护 ,s[0]表示 %5  == 1 的 下标,其他 依次类推  cnt,记录 子树的 元素个数。

想要得到该区间内所有模5等3所有元素的和,左孩子可以求到,两个孩子相互独立,所以求右孩子需要知道(左子树含有 )多少个元素,因为这样分开求的时候,我们才知道 求右孩子时应该求下标模5等几()的所有元素的和。

假如我们 求  i ==  3 时 (表示  %5 == 4),对于 左子树 我们 直接 求 就可以 ,但是 对于 右子树,我们 要知道 在 i== 3 的 情况下 ,右子树的 元素的下标 应该 是 %5 == 几?

p[x].s[i] = p[x*2].s[i] + p[x*2+1].s[((i  - p[x*2].cnt)%5 + 5)% 5];

 

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include< set>
#include<map>
#include<queue>
#include<vector>
#include< string>
#define Min(a,b) a<b?a:b
#define Max(a,b) a>b?a:b
#define CL(a,num) memset(a,num,sizeof(a));
#define eps  1e-12
#define inf   10000000
// freopen("data.txt","r",stdin);
const  double pi  = acos(- 1.0);
typedef   __int64  ll;
const  int maxn =  101000 ;
using  namespace std;
char c[ 6];
struct node
{
     int l;
     int r;
    ll s[ 5] ;
     int cnt ;

}p[maxn* 4] ;
ll  a[maxn],b[maxn],tp[maxn] ;
void build( int x, int l, int r)
{
     p[x].cnt =  0;
     p[x].l = l;
     p[x].r = r;
     CL(p[x].s, 0) ;
     if(l == r) return ;

     int mid = ( l + r) >>  1;
    build(x* 2,l,mid);
    build(x* 2+ 1,mid+ 1,r) ;
}
void add( int x, int pos, int k)
{
    p[x].cnt += k* 2 -  1 ;
     if(p[x].l  == pos && p[x].r == pos)
    {
          p[x].s[ 0] = k*a[pos] ; // 只有 一个元素 所以 % 5 == 1,我们 放到 s[0]里面
           return ;
    }


     int  mid = (p[x].l + p[x].r) >>  1;
     if(pos <=mid )add(x* 2,pos,k);
     else add(x* 2+ 1,pos,k);

     for( int i =  0 ; i<  5;i++)
    {
        p[x].s[i] = p[x* 2].s[i] + p[x* 2+ 1].s[((i  - p[x* 2].cnt)% 5 +  5)%  5] ; // 实现了 动态的 改变
    }

}
int main()
{
    // freopen("data.txt","r",stdin);
    int n ,i;
    while(scanf( " %d ",&n)!=EOF)
   {
       CL(tp,- 1);
        int num =  0 ;
        for(i =  0 ; i < n;i++)
       {
           scanf( " %s ",c);
            if(c[ 0] ==  ' a ')
           {
               tp[i] =  0;
               scanf( " %I64d ",&b[i]);
               a[num++] = b[i] ;
           }
            if(c[ 0] ==  ' d ')
           {
               tp[i] =  1;
               scanf( " %I64d ",&b[i]);
               a[num++] = b[i] ;
           }
            if(c[ 0] ==  ' s ')
           {
               tp[i] =  2 ;
           }
       }
        sort(a,a+num) ;
         int tot = unique(a,a+num) - a ;


        
        build( 1, 0,tot -  1);

         for(i =  0  ; i< n;i++)
        {
             if(tp[i] ==  0)
            {
                add( 1,lower_bound(a,a+tot,b[i]) - a , 1);
            }
             if(tp[i] ==  1)
            {
                  add( 1,lower_bound(a,a+tot,b[i]) - a , 0);
            }
             if(tp[i] ==  2)
             printf( " %I64d\n ",p[ 1].s[ 2]);
        }


   }
}

 

 

 

 

 

转载于:https://www.cnblogs.com/acSzz/archive/2012/09/18/2691668.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值