树状数组 区间update/query

Re

【问题引入】

对于区间修改、区间查询这样的简单问题,打一大堆线段树确实是不划算,今天来介绍一下区间查询+区间修改的树状数组

【一些基础】

树状数组的基本知识不再介绍,请自行百度

我们假设sigma(r,i)表示r数组的前i项和,调用一次的复杂度是log2(i)

设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[i]=sigma(c,i),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v

【今天的主要内容】

我们可以实现NlogN时间的“单点修改,区间查询”,“区间修改,单点查询”,其实后者就是前者的一个变形,要明白树状数组的本质就是“单点修改,区间查询”

怎么实现“区间修改,区间查询”呢?

观察式子:
a[1]+a[2]+...+a[n]

= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n]) 

= n*c[1] + (n-1)*c[2] +... +c[n]

= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])    (式子①)

那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]

每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变

那么

式子①

=n*sigma(c,n) - sigma(c2,n)

于是我们做到了在O(logN)的时间内完成一次区间和查询

一件很好的事情就是树状数组的常数比其他NlogN的数据结构小得多,实际上它的计算次数比NlogN要小很多,再加上它代码短,是OI中的利器

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #define N 200100*8
 6 using namespace std;
 7 typedef long long ll;
 8 int n,m;
 9 ll a[N],c1[N],c2[N];
10 struct io{
11     char op[1<<26],*s;
12     io()
13     {
14         fread(s=op,1,1<<26,stdin);
15     }
16     inline int read()
17     {
18         register int u=0;
19         while(*s<48)s++;
20         while(*s>32)u=u*10+*s++-48;
21         return u;
22     }
23 }ip;
24 #define read ip.read
25 inline int lowbit(int x){return x&(-x);}
26 void add(ll *r,int pos, ll v)
27 {
28     for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;
29 }
30 ll getsum(ll *r,int pos)
31 {
32     ll re=0;
33     for(;pos>0;pos-=lowbit(pos))re+=r[pos];
34     return re;
35 }
36 ll sigma(int r)
37 {
38     ll sum1=r*getsum(c1,r),sum2=getsum(c2,r);
39     return sum1-sum2;
40 }
41 ll query(int x,int y)
42 {
43     return sigma(y)-sigma(x-1);
44 }
45 int flag,x,y;ll k;
46 int main()
47 {
48     n=read();
49     for(int i=1;i<=n;i++)
50     {
51         a[i]=read();
52         add(c1,i,a[i]-a[i-1]);
53         add(c2,i,(i-1)*(a[i]-a[i-1]));
54     }m=read();
55     for(int i=1;i<=m;i++)
56     {
57         flag=read();
58         if(flag==1)
59         {
60             x=read();y=read();k=read();
61             add(c1,x,k);add(c1,y+1,-k);
62             add(c2,x,(x-1)*k);add(c2,y+1,y*(-k));
63         }
64         else
65         {
66             x=read();y=read();
67             printf("%lld\n",query(x,y));
68         }
69     }
70     return 0;
71 }
View Code

230ms全场最快qaq

posted @ 2017-10-13 19:53  Michael_Zhuang  阅读(214)  评论(0编辑  收藏  举报