一个简单的整数问题2(树状数组:区间查询&&区间修改)

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

1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

2、“Q l r”,表示询问 数列中第 l~r 个数的和。

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

输入格式

第一行两个整数N,M。

第二行N个整数A[i]。

接下来M行表示M条指令,每条指令的格式如题目描述所示。

输出格式

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

每个答案占一行。

数据范围

1≤N,M≤105,
|d|≤10000,
|A[i]|≤1000000000

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:

4
55
9
15

 解题过程:

树状数组一般只支持单点查询,要实现区间查询需要借助前缀和差分的一些性质,推导过程如下:

一个简单的整数问题2(树状数组:区间查询&&区间修改)_第1张图片

首先,在输入a[i]时,需要用insert维护一下a[i]对应的差分序列(d[i]=a[i]-a[i-1])

1、对于查询区间和的操作:

用到差分和前缀和的思想,因为树状数组适用于维护一段区间和,所以可以借助树状数组来维护ci和fi的累加和,从而通过以上结论公式算出:

sum(a[i]) (i从1到l-1)和  sum(a[i]) (i从1到r)然后通过前缀和的思想让后者减去前者即可得到sum(a[i])  (i从l到r)

2、对于区间修改操作:

同样是前缀和的思想,让l及其以后的(每次+lowbit得到的)区间都加上增量d,然后把r+1及其以后的减去增量d,这样就实现了让[l,r]加上增量d。

完整代码:

#include 
#include 
#include 
#define int long long 

using namespace std;

const int maxn=1e5+5;

int a[maxn],c[maxn],f[maxn],n,m;

inline int lowbit(int x){return x&(-x);}

void insert(int x,int d)
{
    for(int i=x;i<=n;i+=lowbit(i)){
        c[i]+=d;
        f[i]+=x*d;
    }
}

int query(int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i)){
        res+=(x+1)*c[i];
        res-=f[i];
    }
    return res;
}

signed main()
{
    ios::sync_with_stdio();
    cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        insert(i,a[i]-a[i-1]);
    }
    while(m--){
        int l,r,d;
        char c;
        cin>>c;
        if(c=='Q'){
            cin>>l>>r;
            cout<>l>>r>>d;
            insert(l,d);
            insert(r+1,-d);
        }
    }
    return 0;
}

 

你可能感兴趣的:(树)