从前有个东西叫树状数组,它可以轻易实现一些简单的序列操作,比如单点修改,区间求和;区间修改,单点求值等.

但是我们经常需要更高级的操作,比如区间修改区间查询.这时候树状数组就不起作用了,只能选择写一个2000GB的线段树交上去然后被卡常—–或者另一个选择是写ZKW线段树,会好一些.

再但是…谁告诉你树状数组不能区间修改区间求和?告诉你,树状数组不仅能实现,而且代码依旧那么短小精悍.

今天我们就来研究研究,如何实现这个更划算的数据结构.
我们已经学会了树状数组的基本操作:单点修改区间查询,或区间修改单点查询(不会的话先去自学吧…这篇文章不适合你…).思考,区间修改单点求值是怎么做到的?只需要维护一个新数组c[i]=a[i]-a[i-1],也就是c[]是a[]的差分数组,修改区间[l,r]+v只需

add(l,v);add(r+1,-v) //从l加到了n,r以后的多加了,所以要再进行次r+1到n加-v的操作

即可.求某个值的时候,只需要把差分数组的前缀和求出来,就是要求的了.
领悟了这个操作以后我们发现,化区间为单点的思想精髓就在于差分二字.利用差分思想,区间修改解决了,接下来就是区间求和公式的推导过程:
sum(1,n)
=a[1]+a[2]+a[3]+…+a[n-1]+a[n]
=c[1]+(c[1]+c[2])+…+(c[1]+c[2]+…+c[n])
=n*(c[1]+c[2]+…+c[n])-(0*c[1]+1*c[2]+2*c[3]+…+(n-1)*c[n]).
发现什么了?
我们开第二个树状数组c2,令c2[i]=c[i]*(i-1),那么…

区间修改[l,r]+=v:
add(c[l],v),add(c[r+1],-v);
add(c2[l],(l-1)*v),add(c2[r+1],-r*v);

求前缀和sum(1,n):
sum(1,n)=n*query_c(n)-query_c2(n).

求区间和sum(l,r):
sum(l,r)=sum(r)-sum(l-1).

至此,树状数组已经轻松实现了区间修改区间求和!

例题:luogu 3372线段树模板 这题用线段树写500+ms,拿裸的树状数组311ms就切掉了,代码也养眼得多.至于zkw的效率如何我不太清楚.

顺便:其实一开始建树的时候没必要把原来数组的元素一个个扔进树里,直接维护一个前缀和,然后计算的时候加上这个前缀和就好了.省去了nlogn的建树操作,会快很多.此处为了增强代码可读性,没有加这一句.

参考博客:

https://ahackh.ac.cn/2017/06/25/%E8%89%AF%E5%BF%83%E8%AF%A6%E8%A7%A3%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84%E3%81%AE%E5%8C%BA%E9%97%B4%E4%BF%AE%E6%94%B9%E6%B1%82%E5%92%8C%E6%9C%89%E8%BF%99%E7%A7%8D%E6%93%8D%E4%BD%9C/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 102333
using namespace std;
typedef long long ll;
int n,m;
ll a[N],c1[N],c2[N];
inline int lowbit(int x){return x&(-x);}
void add(ll *r,int pos, ll v)
{
for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;
}
ll getsum(ll *r,int pos)
{
ll re=0;
for(;pos>0;pos-=lowbit(pos))re+=r[pos];
return re;
}
ll sigma(int r)
{
ll sum1=r*getsum(c1,r),sum2=getsum(c2,r);
return sum1-sum2;
}
ll query(int x,int y)
{
return sigma(y)-sigma(x-1);
}
int flag,x,y;ll k;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
add(c1,i,a[i]-a[i-1]);
add(c2,i,(i-1)*(a[i]-a[i-1]));
}
for(int i=1;i<=m;i++)
{
scanf("%d",&flag);
if(flag==1)
{
scanf("%d%d%lld",&x,&y,&k);
add(c1,x,k);add(c1,y+1,-k);
add(c2,x,(x-1)*k);add(c2,y+1,y*(-k));
}
else
{
scanf("%d%d",&x,&y);
printf("%lld\n",query(x,y));
}
}
return 0;
}

  

类似题目: 区间

链接:https://www.nowcoder.com/acm/contest/135/I
来源:牛客网

题目描述

Apojacsleam喜欢数组。

他现在有一个n个元素的数组a,而他要对a[L]-a[R]进行M次操作:

操作一:将a[L]-a[R]内的元素都加上P

操作二:将a[L]-a[R]内的元素都减去P

    最后询问a[l]-a[r]内的元素之和?
    请认真看题干及输入描述。

输入描述:

输入共M+3行:

第一行两个数,n,M,意义如“题目描述”

第二行n个数,描述数组。

第3-M+2行,共M行,每行四个数,q,L,R,P,若q为1则表示执行操作2,否则为执行操作1

第4行,两个正整数l,r

输出描述:

一个正整数,为a[l]-a[r]内的元素之和

输入例子:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5 5
1 2 3 6
0 2 5 5
0 2 5 8
1 4 9 6
2 7
输出例子:
23

-->

示例1

输入

复制

10 5
1 2 3 4 5 6 7 8 9 10
1 1 5 5
1 2 3 6
0 2 5 5
0 2 5 8
1 4 9 6
2 7

输出

复制

23

说明

 
直接裸模板就行
//树状数组(升级版)
#include <cstdio>
#define lowbit(x) (x&-x)
#define ll long long
#define maxn 1000010
using namespace std;
ll n, q, c1[maxn], c2[maxn], num[maxn];
void add(ll *r, ll pos, ll v)
{for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;}
ll sigma(ll *r, ll pos)
{
ll ans;
for(ans=0;pos;pos-=lowbit(pos))ans+=r[pos];
return ans;
}
int main()
{
ll i, j, type, a, b, v, sum1, sum2;
scanf("%lld",&n);
scanf("%lld",&q);
for(i=1;i<=n;i++)
{
scanf("%lld",num+i);
add(c1,i,num[i]-num[i-1]);
add(c2,i,(i-1)*(num[i]-num[i-1]));
}
while(q--)
{
scanf("%lld",&type);
if(type!=1)
{
scanf("%lld%lld%lld",&a,&b,&v);
add(c1,a,v);add(c1,b+1,-v);
add(c2,a,v*(a-1));add(c2,b+1,-v*b);
} else {
scanf("%lld%lld%lld",&a,&b,&v);
add(c1,a,-v);add(c1,b+1,v);
add(c2,a,-v*(a-1));add(c2,b+1,v*b);
}
}
scanf("%lld%lld",&a,&b);
sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1);
sum2=b*sigma(c1,b)-sigma(c2,b);
printf("%lld\n",sum2-sum1);
return 0;
}

  

树状数组求区间和模板 区间可修改 参考题目:牛客小白月赛 I 区间的更多相关文章

  1. poj 3486 A Simple Problem with Integers(树状数组第三种模板改段求段)

    /* 树状数组第三种模板(改段求段)不解释! 不明白的点这里:here! */ #include<iostream> #include<cstring> #include< ...

  2. 树状数组求逆序对:POJ 2299、3067

    前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换 ...

  3. UVA11525 Permutation[康托展开 树状数组求第k小值]

    UVA - 11525 Permutation 题意:输出1~n的所有排列,字典序大小第∑k1Si∗(K−i)!个 学了好多知识 1.康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+ ...

  4. poj 2299 Ultra-QuickSort(树状数组求逆序数+离散化)

    题目链接:http://poj.org/problem?id=2299 Description In this problem, you have to analyze a particular so ...

  5. 牛客练习赛33 D tokitsukaze and Inverse Number (树状数组求逆序对,结论)

    链接:https://ac.nowcoder.com/acm/contest/308/D 来源:牛客网 tokitsukaze and Inverse Number 时间限制:C/C++ 1秒,其他语 ...

  6. HDU 1394 Minimum Inversion Number ( 树状数组求逆序数 )

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 Minimum Inversion Number                         ...

  7. POJ2985 The k-th Largest Group[树状数组求第k大值+并查集||treap+并查集]

    The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8807   Accepted ...

  8. 树状数组求第k小的元素

    int find_kth(int k) { int ans = 0,cnt = 0; for (int i = 20;i >= 0;i--) //这里的20适当的取值,与MAX_VAL有关,一般 ...

  9. POJ2299Ultra-QuickSort(归并排序 + 树状数组求逆序对)

    树状数组求逆序对   转载http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 转载: 树状数组,具体的说是 离散化+树 ...

随机推荐

  1. git指令-未完待更新

    git指令 1. $ git config --global user.name "Your Name" $ git config --global user.email &quo ...

  2. 机房ping监控 smokeping+prometheus+grafana(续) 自动获取各省省会可用IP

    一.前言 1.之前的文章中介绍了如何使用smokeping监控全国各省的网络情况:https://www.cnblogs.com/MrVolleyball/p/10062231.html 2.由于之前 ...

  3. 小伙子,你真的清楚 JVM GC ?

    序 正文 如何确定垃圾? 前面已经提到 JVM 可以采用 引用计数法 与 可达性分析算法 来确定需要回收的垃圾,我们来具体看一下这两种算法: 引用计数法 该方法实现为:给每个对象添加一个引用计数器,每 ...

  4. [nghttp2]压测工具,源码编译并进行deb打包过程

    编译环境:deepin 15.11桌面版 nghttp2下载地址:https://github.com/nghttp2/nghttp2 环境要求 emm只能在类Linux环境才能完整编译,想在Wind ...

  5. 从原理层面掌握@RequestAttribute、@SessionAttribute的使用【一起学Spring MVC】

    每篇一句 改我们就改得:取其精华,去其糟粕.否则木有意义 前言 如果说知道@SessionAttributes这个注解的人已经很少了,那么不需要统计我就可以确定的说:知道@RequestAttribu ...

  6. IE浏览器主页被篡改为2345,针对一般解决办法无法解决的情况

    1.注册表修改 按微软键+R  输入regedit 弹出注册表.HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN 将右侧的Sta ...

  7. 【Kubernetes 系列四】Kubernetes 实战:管理 Hello World 集群

    目录 1. 创建集群 1.1. 安装 kubectl 1.1.1. 安装 kubectl 到 Linux 1.1.2. 安装 kubectl 到 macOS 1.1.3. 安装 kubectl 到 W ...

  8. 算法与数据结构基础 - 回溯(Backtracking)

    回溯基础 先看一个使用回溯方法求集合子集的例子(78. Subsets),以下代码基本说明了回溯使用的基本框架: //78. Subsets class Solution { private: voi ...

  9. JavaWeb——Servlet开发1

    Java Servlet是运行在服务器端上的程序,Servlet是Java Servlet包中的一个接口,能够直接处理和相应客户端的请求,也可以将工作委托给应用的其他类. public interfa ...

  10. arukas 樱花免费docker容器获取IP和端口

    arukas 樱花免费docker容器,可以安装linux系统,但是每隔一段时间会重启,重启以后IP地址和映射到公网的端口都会变,获取IP和端口,我研究了很久终于找到了C#获取IP和端口的办法,用来搭 ...