树状数组区间修改和区间求和
Posted cyheimu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组区间修改和区间求和相关的知识,希望对你有一定的参考价值。
最一般树状数组能做到的操作是单点修改,区间求和,都是log(n)级别的。原理就是用树状数组维护a[i]的部分和。
想要做到修改区间,求单点值也很简单,用树状数组维护a[i]的差分数组d[i]的部分和既可。
那么,如何同时做到区间求和,区间修改呢?
有人可能会说了,如果是区间求和区间修改的话,直接写线段树不就好了吗?
但是,从代码长度来看(dalao请无视),显然使用树状数组在考场上出错率会小一些,而且比较爽QWQ。
回归正题。我们知道,想要达到log(n)级别的区间修改需要修改d[i],那么在修改d[i]的情况下想要区间求和,我们就应该维护这个:
找出d[i]与a[i]的前缀和的关系,并用树状数组维护这个关系。
那么如何找关系呢?这里需要一点点数学证明:
我们设sum(i)=a[1]+a[2]+a[3]+······+a[i],
则可知
sum(i)=(d[1])+(d[1]+d[2])+(d[1]+d[2]+d[3])+······+(d[1]+d[2]+d[3]+······+d[i]),
所以
sum(i)=i*d[1]+(i-1)*d[2]+(i-2)*d[3]+······+1*d[i],
所以
sum(i)=∑(j从1到i)(i-j+1)d[j],
最后一步化简后,我们得到这样一个式子:
sum(i)=(i+1)∑(j从1到i)d[j]-∑(j从1到i)j*d[j]
即我们需要用树状数组分别维护d[i]的和和i*d[i]的和既可。
下面上代码:
1#include <iostream>
2#include <cstdio>
3#include <cstdlib>
4#include <algorithm>
5using namespace std;
6
7long long n,m;
8long long c1[100010],c2[100010];
9
10void update(long long x,long long k)
11
12 long long i=x;
13 while(x<=n)
14
15 c1[x]+=k;
16 c2[x]+=i*k;
17 x+=x&-x;
18
19 return;
20
21
22long long sum(long long x)
23
24 long long ans=0;
25 long long i=x;
26
27 while(x>0)
28
29 ans+=c1[x]*(i+1);
30 ans-=c2[x];
31 x-=x&-x;
32
33 return ans;
34
35
36int main()
37
38 long long ord,x,y,k;
39 long long a,b=0;
40
41 scanf("%lld%lld",&n,&m);
42 for(long long i=1;i<=n;i++)
43
44 scanf("%lld",&a);
45 update(i,a-b);
46 b=a;
47
48 for(long long i=1;i<=m;i++)
49
50 scanf("%lld",&ord);
51 if(ord-1)
52
53 scanf("%lld%lld",&x,&y);
54 printf("%lld\n",sum(y)-sum(x-1));
55
56 else
57
58 scanf("%lld%lld%lld",&x,&y,&k);
59 update(x,k);
60 update(y+1,-k);
61
62
63 return 0;
64
以上是关于树状数组区间修改和区间求和的主要内容,如果未能解决你的问题,请参考以下文章