树状数组 区间修改 全网最简单最清楚

来源:互联网 发布:linux重启命令init 编辑:程序博客网 时间:2024/06/04 20:08

愚蠢的我花了好久好久才看明白他的意思….
最近不是在弄树状数组嘛,然后这个区间修改,怎么也想不明白,今天看了一下午博客,总算弄懂了.
让我们开始.我会尽量使用常数而不是变量,为了让你完全清楚.
如果出现公式,都不难,如果你一时看不清,可以拿笔写一写,保证没有任何难度.
我们约定,a[i]代表数据本身,(为了和数组数组中的数据区别)
首先,我们要实现区间修改,那么我们就不能和以前一样,我们假设一个c数组,这里面c[i],代表着a[i]-a[i-1].

问题来到,为什么我们要构造c[i].
想一想,假如我要对a[l]+…+a[r]进行区间修改,怎么办?
首先,c[i],是a[i]数组的一个映射,也就是说,当我们使用a[i]来建立树状数组时,a[i]表现的是每个节点的数字本身,而我们的c[i],则是表现每个节点的数字之间的差.那么我们可以想象,假如a[l]~a[r],每个都加x,那么在用a[i]表示节点数字性质时,我们理所当然的就是每个都加x,但是我们现在用c[i]表示节点数字性质时,你会发现,除了c[l]会加x,c[r+1]会减x外,其他地方,都没有变化.这便是关键了.
结论,利用c[i]作为a[i]的映射,我们可以通过两次修改,达到原来的n次修改的效果.那么接下来,就是我们区间修改的具体实现了.

可以认为接下来的一切,并没有显著的逻辑,他只是就是这样而已.

当我们有了这个定义之后,显然的,a[2] = c[2]+c[1],a[3] = c[3]+c[2]+c[1].那么这时候,我们想要计算a[1]+a[2]+….+a[n],也就是
c1 + (c2+c1)+(c3+c2+c1)+(…)+(Cn+Cn-1+…+C1),对吧?

我们可以进行整合,将他变为
n*(c1+c2+c3+…+cn)-(0*c1+1*c2+2*c3+…+(n-1)*Cn).
为什么这里关键?你观察会发现,这是两个前缀和的形式,一个是c1+c2+..+cn,一个是(1-0)*c1+(2-1)*c2+…+(n-1)*Cn.
那么说道前缀和,我们树状数组要上场了!
我们不妨假设d[i]= (i-1)*c[i].
显然,我们维护一个c[i]数组对应的树状数组(注意他们两个不是一个东西),就可以很快的对c[i]进行求和,同时,建立c[i]的映射,d[i]的树状数组,也可以很快求出他的前缀和了.

具体看代码,这是一个模版题的代码.
codevs,区间修改区间求和

/*  xzppp  */#include <iostream>#include <vector>#include <cstdio>#include <string.h>#include <algorithm>#include <queue>#include <map>#include <string>#include <cmath>#include <bitset>#include <iomanip>using namespace std;#define FFF freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define MP make_pair#define PB push_backtypedef long long  LL;typedef unsigned long long ULL;typedef pair<int,int > pii;typedef pair<double,double > pdd;typedef pair<double,int > pdi;const int MAXN = 200000+17;const int MAXM = 20;const int MAXV = 2*1e3+17;const int INF = 0x7fffffff;const int MOD = 1e9+7;LL bit[2][MAXN],n,a[MAXN];void add(int k,int p,LL x){    while(p<=n)    {        bit[k][p] += x;        p += p&-p;    }}LL sum(int k,int p){    LL res = 0;    while(p>0)    {        res += bit[k][p];        p -= p&-p;    }    return  res;}void additv(int from,int to,LL x){    add(0,from,x);    add(0,to+1,-x);    add(1,from,(from-1)*x);    add(1,to+1,(to)*(-x));}LL sumitv(int from,int to){    LL res = 0;    res += to*sum(0,to);    res -= sum(1,to);    res -= (from-1)*sum(0,from-1);    res += sum(1,from-1);    return res;}int main(){    #ifndef ONLINE_JUDGE     FFF    #endif    int m;    cin>>n;    for (int i = 1; i <= n; ++i)    {        scanf("%lld",a+i);        add(0,i,a[i]-a[i-1]);        add(1,i,(i-1)*(a[i]-a[i-1]));    }    cin>>m;    for (int i = 0; i < m; ++i)    {        LL cmd,a,b,c;        scanf("%lld",&cmd);        if(cmd==1)        {            scanf("%lld%lld%lld",&a,&b,&c);            additv(a,b,c);        }        else        {            scanf("%lld%lld",&a,&b);            printf("%lld\n",sumitv(a,b));        }    }    return 0;}
原创粉丝点击