前缀和
整体思想:前缀和是一种动态规划的思想。模拟出对应的计算公式。
一维前缀和
作用:求任意[l, r]区间的和。时间复杂度O(1)
前缀和公式: s[i] = s[i - 1] + a[i]
部分和公式[l, r]:s[r] - [l - 1]
例题1:截断数组
给定一个长度为 n 的数组 a1,a2,…,an。
现在,要将该数组从中间截断,得到三个非空子数组。
要求,三个子数组内各元素之和都相等。
请问,共有多少种不同的截断方法?
输入格式
第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
输出一个整数,表示截断方法数量。
数据范围
前六个测试点满足 1 ≤ n ≤ 10。
所有测试点满足 1 ≤ n ≤ 105,−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
思路
首先预处理出前缀和:s[n] / 3表示三段中每一段的和
枚举第三段分割的位置,因为每一段都非空,第一段的和可以看成s[i - 2]
找出所有满足第一段和等于s[n] / 3的cnt数量
再去检验第三段的和s[n] - s[i - 1]是否等于s[n] / 3,满足即可将cnt加到答案中
代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n;
int s[N], cnt[N];
int main()
{
cin >> n;
LL sum = 0;
// 处理前缀和
for (int i = 1; i <= n; i ++)
{
cin >> s[i];
s[i] += s[i - 1];
}
if (s[n] % 3)
{
cout << 0 << endl;
return 0;
}
LL res = 0;
// 枚举第三段的起点
for (int i = 3, cnt = 0; i <= n; i ++)
{
if (s[i - 2] == s[n] / 3) cnt ++;
if (s[n] - s[i - 1] == s[n] / 3) res += cnt;
}
cout << res << endl;
return 0;
}