Intro:

在OI中,前缀和是一种泛用性很高的数据结构,也是非常重要的优化思想


Function: 求静态区间和

模板题:输入序列 \(a_{1..n}\) ,对于每一个输入的二元组 \((l,r)\) ,求 \(\sum_{i=l}^ra_i\)

先想一想朴素算法怎么做吧

对于输入的每一组 \((l,r)\) ,遍历序列 \(a_{l..r}\) 求和,代码如下

int s(0);
for(int i(l);i<=r;++i)s+=a[i];
return s;

Time complexity: \(O(n)\)

Memory complexity: \(O(n)\)

如果有m次询问,则总时间复杂度 \(O(nm)\)

观察这个过程,可以发现有大量多余运算,比如说,对于两次询问,它们区间的交集就是被多余运算的

那么有没有方法使得计算量减少呢?

可以发现 \(\sum_{i=l}^ra_i=\sum_{i=1}^ra_i-\sum_{i=1}^{l-1}a_i\)

也就是说如果存在数组 \(s_{1..n}\) 使得 \(s_i=\sum_{j=1}^ia_j\) ,则

\(\sum_{i=l}^ra_i=s_r-s_{l-1}\)

\(s_{1..n}\) 就是传说中的前缀和数组啦!


Operation:

First: 为数组 \(a_{1..n}\) 构造前缀和数组 \(s_{1..n}\)

Second: 对于输入区间 \([l,r]\) ,直接计算出区间和 \(s_r-s_{l-1}\)

关键就是前缀和数组如何构造

使用递推思想

\(s_i=\sum_{j=1}^ia_j=\sum_{j=1}^{i-1}a_j+a_i=s_{i-1}+a_i\)


Code:

求\(s_{1..n}\)

for(int i(1);i<=n;++i)s[i]=s[i-1]+a[i];

询问

return s[r]-s[l-1];

Time complexity: \(O(n)\) 预处理 \(O(1)\) 查询

Memory complexity: \(O(n)\)

P.s 可以直接将原数组变成前缀和数组,则不需要额外空间,代码如下

for(int i(2);i<=n;++i)a[i]+=a[i-1];

Example:

洛谷P1114 “非常男女”计划

这是一道练习前缀和思想(然而不是特别明显)的经典题目

如果将男生表示 \(1\) ,女生表示 \(-1\) ,那么题目就变成了求能使区间和为\(0\)的最大区间长度(是不是有点前缀和的味道了)

但是单单枚举区间两端, \(O(n^2)\) 的时间复杂度明显超时,而且这种方法根本不需要前缀和(直接求和即可)

这道题的要点在于 \(\sum_{i=l}^ra_i=\sum_{i=1}^ra_i-\sum_{i=1}^{l-1}a_i\) (是不是很眼熟)

所以当 \(\sum_{i=l}^ra_i=0\) 时, \(\sum_{i=1}^ra_i=\sum_{i=1}^{l-1}a_i\)

所以关键在于对于两个端点 \(l,r\) ,如果 \(s_l=s_r\) ,那么 \([l+1,r]\) 就是一个可行区间

对所有端点\(i\),按照\(s_i\)分类,对每一类求极差,取最大值即可

P.s 其中端点 \(0\) 属于 \(s_i=0\) 类

具体见代码( \(s_{1..n}\) 被优化掉了,用 \(p_i,-n\leqslant i\leqslant n\) 表示每一个 \(s_j=i\) 的最小 \(j\) ,若不存在则 \(p_i=-1\) )

//This program is written by Brian Peng.
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
#define Rd(a) (a=read())
#define Gc(a) (a=getchar())
#define Pc(a) putchar(a)
inline int read(){
register int x;register char c(getchar());register bool k;
while(!isdigit(c)&&c^'-')if(Gc(c)==EOF)exit(0);
if(c^'-')k=1,x=c&15;else k=x=0;
while(isdigit(Gc(c)))x=(x<<1)+(x<<3)+(c&15);
return k?x:-x;
}
void wr(register int a){
if(a<0)Pc('-'),a=-a;
if(a<=9)Pc(a|'0');
else wr(a/10),Pc((a%10)|'0');
}
signed const INF(0x3f3f3f3f),NINF(0xc3c3c3c3);
long long const LINF(0x3f3f3f3f3f3f3f3fLL),LNINF(0xc3c3c3c3c3c3c3c3LL);
#define Ps Pc(' ')
#define Pe Pc('\n')
#define Frn0(i,a,b) for(register int i(a);i<(b);++i)
#define Frn1(i,a,b) for(register int i(a);i<=(b);++i)
#define Frn_(i,a,b) for(register int i(a);i>=(b);--i)
#define Mst(a,b) memset(a,b,sizeof(a))
#define File(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define N (200010)
#define P(a) (p[a+n])
int n,p[N],s,ans;
signed main(){
Rd(n),Mst(p,-1),P(0)=0;
Frn1(i,1,n)s+=read()?1:-1,~P(s)?ans=max(ans,i-P(s)):P(s)=i;
wr(ans),exit(0);
}

到此为止前缀和的所有基本操作都讲完啦!


Conclusion & Extension:

前缀和是一种泛用性很高的数据结构,也是非常重要的优化思想

它利用预处理和递推的方法减少多余运算,达到优化的目的

不仅适用于加法,还适用于所有满足于结合律而且具有单位(对于加法就是 \(0\) )和逆元(对于加法 \(a\) 的逆元是 \(-a\) )的二元运算,如乘法

但是对于求最值就不太靠谱了(对于这类问题(称为RMQ问题)也有很棒的算法)

前缀和数组的online version:树状数组

Particularly, 前缀和还有逆运算:差分

到此为止本篇文章就圆满结束啦,请各位奆佬们多多指教和支持,THX!

Basic Thought / Data Structure: 前缀和 Prefix Sum的更多相关文章

  1. ✡ leetcode 170. Two Sum III - Data structure design 设计two sum模式 --------- java

    Design and implement a TwoSum class. It should support the following operations: add and find. add - ...

  2. Basic Thought / Data Structure: 差分 Difference

    Intro: 作为查询界的\(O(1)\)王者--前缀和的亲兄弟,差分,他可是修改界的\(O(1)\)王者 Prerequisite knowledge: 前缀和 Function: 仅单次询问的区间 ...

  3. 字典树(查找树) leetcode 208. Implement Trie (Prefix Tree) 、211. Add and Search Word - Data structure design

    字典树(查找树) 26个分支作用:检测字符串是否在这个字典里面插入.查找 字典树与哈希表的对比:时间复杂度:以字符来看:O(N).O(N) 以字符串来看:O(1).O(1)空间复杂度:字典树远远小于哈 ...

  4. LeetCode208 Implement Trie (Prefix Tree). LeetCode211 Add and Search Word - Data structure design

    字典树(Trie树相关) 208. Implement Trie (Prefix Tree) Implement a trie with insert, search, and startsWith  ...

  5. [LeetCode] Two Sum III - Data structure design 两数之和之三 - 数据结构设计

    Design and implement a TwoSum class. It should support the following operations:add and find. add - ...

  6. LeetCode Two Sum III - Data structure design

    原题链接在这里:https://leetcode.com/problems/two-sum-iii-data-structure-design/ 题目: Design and implement a ...

  7. hdu-5929 Basic Data Structure(双端队列+模拟)

    题目链接: Basic Data Structure Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 65536/65536 K (Ja ...

  8. LeetCode 笔记27 Two Sum III - Data structure design

    Design and implement a TwoSum class. It should support the following operations: add and find. add - ...

  9. HDU 5929 Basic Data Structure 模拟

    Basic Data Structure Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

随机推荐

  1. 常用linux 命令

    ls -lt 时间倒序 ls -ltr 时间正序 ls -lS 大小倒序 ls -li 显示inode ----------------- cat smb_quicktest.py| grep -E ...

  2. Python3使用Pyintaller-打包成exe

    Pyinstaller打包exe执行文件 安装Pyinstaller 使用pip安装Pyinstaller 用管理员模式运行cmd,输入命令: pip install pyinstaller 此方法会 ...

  3. 「洛谷P1343」地震逃生 解题报告

    P1343 地震逃生 题目描述 汶川地震发生时,四川XX中学正在上课,一看地震发生,老师们立刻带领x名学生逃跑,整个学校可以抽象地看成一个有向图,图中有n个点,m条边.1号点为教室,n号点为安全地带, ...

  4. Codeforces Round #612 (Div. 2)

    https://codeforces.com/contest/1287/ A - Angry Students 题意:求A后面的P最长连续有几个? 题解:? int n; char s[200005] ...

  5. 1046 划拳 (15 分)C语言

    划拳是古老中国酒文化的一个有趣的组成部分.酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字.如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢了,输家罚一杯酒.两人同赢或两人同输 ...

  6. Go网络文件传输

    流程分析 借助TCP完成文件的传输,基本思路如下: 发送方(客户端)向服务端发送文件名,服务端保存该文件名. 接收方(服务端)向客户端返回一个消息ok,确认文件名保存成功. 发送方(客户端)收到消息后 ...

  7. HashMap,HashTable 区别,实现原理。

    HashMap是HashTable 的轻量级,非线程安全的,都是实现了map接口 区别:hashmap 允许空键值对的存在,非线程安全,效率高于hashtable,因为hashtable 是synch ...

  8. 【转】ArcGIS Server 10.1 动态图层—更改风格

    在 ArcGIS Server REST API中我们可以通过向Graphicslayer中添加临时图元的方法来完成对显示结果的渲染:如果仅仅是更改原有地图服务显示风格,在ArcGIS10.1下使用动 ...

  9. 如何编写Robot Framework测试用例2---(测试用例语法1)

    基本语法 测试用例由关键字组成,关键字的来源有三种: 1从测试库引入:2从资源文件引入:3从关键字表中引入(自定义关键字) 下面就是一个典型的测试用例组织形式. 图中有2个测试用例“Valid Log ...

  10. 异数OS TCP协议栈测试(四)--网卡适配篇

    异数OS TCP协议栈测试(四)–网卡适配篇 本文来自异数OS社区 github: https://github.com/yds086/HereticOS 异数OS社区QQ群: 652455784 异 ...