VJ A Simple Problem with Integers(区间修改,区间查询)

根据树状数组的“区间修改,单点查询”我们知道用差分思想维护的树状数组 b , b[i] 的前缀和就是原数组 a[x] 增加的值,那么序列 a 的前缀和 a[1~x] 整体增加的值就是区间[1~x]的增加的值

原理不写了,直接写方法

在本题中,我们可以增加一个树状数组,用于维护 i*b[i] 的 前缀和。

我们建立两个数组 c[0][N] c[1][N], 即 c0, c1,起初赋值为零,c0 为 b[i] 前缀和
对于每次的add操作"C l r d",我们可分为下面4步:

  1. 对 c0 进行 l 上加上 d
  2. 对 c0 进行 r+1 上减去 d
  3. 对 c1 进行 l 上加上 d*l
  4. 对 c1 进行 r+1 上减去 d*(r+1)

我们还要建立数组sum存放a的前缀和
对于指令"Q l r"
有ans = (sum[r]+(r+1) * ask(c0 ,r)-ask(c1 ,r))-(sum[l-1]+l * ask(c0 ,l-1)-ask(c1 ,l-1))

AC代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll  n,c[2][200010],a[200010],sum[200010];
void add(ll k,ll x,ll y)
{
   for(;x<=n;x+=x&-x)
    c[k][x]+=y;
}
ll ask(ll k,ll x)
{
   ll ans=0;
   for(;x;x-=x&-x)
    ans+=c[k][x];
   return ans;
}
int main()
{
    ll m;
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
    {
      scanf("%lld",&a[i]);
      sum[i]=sum[i-1]+a[i];
    }
    while(m--)
    {
       char ch;
       char s[2];
       scanf("%s",s);
       if(s[0]=='C')
       {
          ll l,r,d;
          scanf("%lld%lld%lld",&l,&r,&d);
          add(0,l,d);
          add(0,r+1,-d);
          add(1,l,l*d);
          add(1,r+1,-(r+1)*d);
       }
       else
       {
          ll l,r;
          scanf("%lld%lld",&l,&r);
          ll ans=sum[r]+(r+1)*ask(0,r)-ask(1,r);
          ans-=sum[l-1]+l*ask(0,l-1)-ask(1,l-1);
           printf("%lld\n",ans);
       }
    }
return 0;
}

posted @ 2020-03-04 16:26  Pecoz  阅读(126)  评论(0编辑  收藏  举报