【区间操作 区间查询 差分】 A Simple Problem with Integers plus

传送门

题意

给定一个长度为 \(N\) 的序列 \(A\),以及 \(M\) 条指令,每条指令可能是以下两种之一:

  • \((C , l , r , d)\),表示把序列第 \(l\sim r\) 项都加上 \(d\)
  • \((Q , l , r)\),表示询问 数列中第 \(l\sim r\) 个数的和。

对于每个询问,输出一个整数表示答案。

数据范围

\(1\leq n,m\leq 10^{5}\)
\(| d | \leq 10000\)
\(A\left[ i\right] \leq 10^{9}\)

题解

维护一个差分序列 \(b\)

  • \(\sum_{i=1}^{x} b[i]\)\(x\) 处值的增减
  • \(\sum_{i=1}^{x} \sum_{j=1}^{i} b[j]\)\(x\) 的前缀和增减,变形得
    • \(\sum_{i=1}^{x} \sum_{j=1}^{i} b[j]=\sum_{i=1}^{x}(x-i+1) \times b[i]=(x+1) \sum_{i=1}^{x} b[i]-\sum_{i=1}^{x} i \times b[i]\)
    • \((x-i+1)\) 指每个 \(b[i]\) 总共被计算的次数为

思想:分离含有多个变量的项,使公式中不同变量之间互相独立
两个树状数组 \(c_{0}\)\(c_{1}\) 分别维护 \(b\left[i\right]\)\(i\times b\left[i\right]\) 的前缀和

  • \(c_{0}\) 对应 \(b\left[i\right]\)
  • \(c_{1}\) 对应 \(i\times b\left[i\right]\)
    • \(c_{0}\) 普通差分
    • \(c_{1}\) 中将 \(l\) 位置的 \(+l\times d\) , \(r+1\) 位置的 \(-(r+1)\times d\)

同时求出一个 \(a\) 的前缀和每次求都通过这三个数组即可完成区间修改和区间查询的操作

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
ll sum[N];
ll c[2][N];
int n,m;
ll lowbit(ll x){ return x & -x; }
void add(int k,int x,int d){
    for(int i=x;i<=n;i+=lowbit(i))
        c[k][i]+=d;
}
ll ask(int k,int x){
    ll res=0;
    for(int i=x;i;i-=lowbit(i))
        res+=c[k][i];
    return res;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin >> sum[i],sum[i]+=sum[i-1];
    while(m--){
        char op[2]; cin>>op;
        if(op[0]=='C'){
            int l,r,d; cin>>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{
            int l,r; cin>>l>>r;
            ll ans=sum[r]+(r+1)*ask(0,r)-ask(1,r);
            ans-=sum[l-1]+(l-1+1)*ask(0,l-1)-ask(1,l-1);
            cout<<ans<<endl;
        }
    }
}
posted @ 2020-05-20 21:39  Hyx'  阅读(200)  评论(0)    收藏  举报