前缀和分为一维前缀和与二维前缀和;
一维前缀和就是前n项和,通过前缀和的减法可以求出一段区间的值:
实用技巧:1.s[i]+=s[i-1]
2.下标从1开始
3.输入与预处理同时进行
常用公式:s[i]+=s[i-1];
a数组储存原始数据,s储存a的前n项和;
上面这个公式是经过s[i]=s[i-1]+a[i]变形后得到的,在实际中最为常用;
因为出现了i-1所以下标从1开始省去处理边界,大部分题目都以i=1开始最为实用;
经过极简处理可用s数组储存数据,并且在数据输入阶段就进行前缀和的预处理,例如:
const int N=1e5+10;
int n;
int s[N];
for(int i=1;i<=n;i++){
scanf("%d",&s[i]);
s[i]+=s[i-1];
}
二维前缀和就是当前点与原点围成的矩形的数据之和;
常用公式:
有一个矩阵a[n][m](下标从1开始),以一对整数i,j,确定其前缀和以二维数组s[n][m]储存,既左上角的部分
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]
由点(x1,y1),点(x2,y2)围成的矩形
s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]
今日份刷题:截断数组,前缀和,子矩阵和,激光炸弹,区域和检索,数组的中心下标,前缀和序列。
其中截断数组做蒙了,其他的感觉都只是简单的应用。
截断数组:
给定一个长度为 n 的数组 a1,a2,…,an。
现在,要将该数组从中间截断,得到三个非空子数组。
要求,三个子数组内各元素之和都相等。
请问,共有多少种不同的截断方法?
输入格式
第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
输出一个整数,表示截断方法数量。
数据范围
前六个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤1e5,−10000≤ai≤10000。
输入样例1:
4
1 2 3 3
输出样例1:
1
输入样例2:
5
1 2 3 4 5
输出样例2:
0
输入样例3:
2
0 0
输出样例3:
0
代码:
#include <iostream>
using namespace std;
const int N=1e5+10;
typedef long long LL;
int n,s[N];
int main(){
cin>>n;
if(n<3){
puts("0");
return 0;
}
for(int i=1;i<=n;i++){
scanf("%d",&s[i]);
s[i]+=s[i-1];
}
int sum=s[n];
if(sum%3){
puts("0");
return 0;
}
LL res=0;//如果有1e5个0,组合个数可能会爆int
int cnt=0;//记录前i个前缀和满足s[n]/3的个数;
for(int i=1;i<=n-2;i++){//两刀构成一种切法,注意有负数;
if(s[i]==sum/3) cnt++;//前i前缀和,满足第一刀的切法
if(sum-s[i+1]==sum/3) res+=cnt;//当满足一次第二刀切法时,与第一刀的切法构成cnt种切法
}
cout<<res<<endl;
return 0;
}
题解:
首先要对总合进行特判,如果不满足是3的倍数,就直接排除;
关键点为代码61行,用cnt记录枚举过的数据中每个前缀和满足s[n]/3的次数,也就是第一段;
代码62行寻找第三段满足s[n]/3的点 ( 当第三段和第一段都满足时,中间一段自然满足s[n]/3 ),
此时如果发现有满足第三段i的位置,就与第一段的cnt种情况相匹配;
易错点:
1.数据有负数;
2.数据范围,假设全部数据都为当前数据可达到的最大值1e4,数据总量为1e5,所以最后一个前缀 和为1e9,又int的最大值为1e9,所以会"爆int",需设置成long long即可;
前缀和总结:主要是在数据范围与内存上出错,容易溢出与超出内存。