cf86D 分块

给定一个数列:A1, A2,……,An,定义Ks为区间(l,r)中s出现的次数。

t个查询,每个查询l,r,对区间内所有a[i],求sigma(Ks^2*a[i])

离线+分块

将n个数分成sqrt(n)块。

对所有询问进行排序,排序标准:

      1. Q[i].left /block_size < Q[j].left / block_size (块号优先排序)

      2. 如果1相同,则 Q[i].right < Q[j].right (按照查询的右边界排序)

从上一个查询后的结果推出当前查询的结果。

如果一个数已经出现了x次,那么需要累加(2*x+1)*a[i],因为(x+1)^2*a[i] = (x^2 +2*x + 1)*a[i],x^2*a[i]是出现x次的结果,(x+1)^2 * a[i]是出现x+1次的结果。

typedef long long LL ;

const int  maxn = 1000008 ;

struct  Q{
        int l , r , id , p ;
        friend bool operator < (const Q A , const Q B){
             if(A.p == B.p) return A.r < B.r ;
             else  return A.p < B.p ;
        }
}q[200008] ;

LL   a[maxn] ;
LL   cnt[maxn] ;
LL   ans[maxn] ;
int  L , R  ; LL  sum ;
LL   ask(int l , int r , int id){
     int i ;
     if(id == 0){
         sum = 0 ;
         for(i = l ; i <= r ; i++){
             sum += a[i] * (cnt[a[i]]*2 + 1) ;
             cnt[a[i]]++ ;
         }
         L = l , R = r ;
         return sum ;
     }
     for(i = l ; i < L ; i++){
           sum += a[i] * (cnt[a[i]]*2 + 1) ;
           cnt[a[i]]++ ;
     }
     for(i = L ; i < l ; i++){
           sum += a[i] * (-cnt[a[i]]*2 + 1) ;
           cnt[a[i]]-- ;
     }
     for(i = R+1 ; i <= r ; i++){
           sum += a[i] * (cnt[a[i]]*2 + 1) ;
           cnt[a[i]]++ ;
     }
     for(i = r+1 ; i<= R ; i++){
           sum += a[i] * (-cnt[a[i]]*2 + 1) ;
           cnt[a[i]]-- ;
     }
     L = l , R = r ;
     return sum ;
}

int  main(){
     int i  , j  , n  , m ;
     int blocksize ;
     while(scanf("%d%d" , &n , &m) != EOF){
          for(i = 1 ; i <= n ; i++) scanf("%I64d" ,&a[i]) ;
          memset(cnt , 0 , sizeof(cnt)) ;
          blocksize = sqrt(0.5 + n) ;
          for(i = 0 ; i < m ; i++){
               scanf("%d%d" , &q[i].l , &q[i].r) ;
               q[i].id = i ;
               q[i].p = q[i].l / blocksize ;
          }
          sort(q , q+m) ;
          for(i = 0 ; i < m ; i++)
              ans[q[i].id] = ask(q[i].l , q[i].r , i) ;
          for(i = 0 ; i < m ; i++) printf("%I64d\n" , ans[i]) ;
     }
     return 0 ;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值