本题需要把一个数组截断成三个非空部分,并且每个部分内之和要相等,即每部分之和等于s[n]/3 (注意s[n]是前缀和,简单理解就是1到n的所有数之和);n 小于1e5
所以首先可以进行特判,判断总和能否被3整除;
if (s[n]%3)
{
puts("0");
return 0;
}
如果可以再继续操作,不然直接输出0;
接下来,因为截成三部分需要两刀,但是两刀的位置都不确定,所以需要先确定一个再确定第二个;
要注意两刀的位置是不可能重合的,并且极端的情况是第一刀在n-2的位置,第二刀在n-1的位置
因为要保证每一部分至少有一个元素;
确定方法:
首先i从3开始递增到n(前面说的第一刀只能到达n-2的位置)
第一个if:如果出现了第一刀往左的数,也就是第一部分等于s[n]/3,那么这里使用一个cnt ++ 来记录,表示从1到i-2的途中,出现了cnt个符合第一刀条件的位置;
if(s[i-2] == s[n]/3) cnt++;
第二个if:由于第一刀的方案次数都被记下来了(并且随着i-2不断递增,第一刀的方案次数还可能继续增多),所以当i-1这个指针运行到满足第二刀的条件时,也就是s[n]-s[i-1] = s[n]/3时;
即可以说明这里就是第二刀位置的一种方案了;
那么在这第二刀属于这个位置的方案下 ,一共有多少种第一刀的可能呢?
也就是前面的cnt(再次联想,为什么第二刀用的是i-1,因为第一刀此时可能就在第二刀的前面,如果用的不是i-1,而是i-2,那么第一刀和第二刀重合,此时这种方案应该不存在,那么就会导致多算1),所以说在当前第二刀的位置下,有cnt(随着第二刀推移,可能增多)种可能,那么用res来记录,即:
if(s[n]-s[i-1] == s[n]/3) res += cnt;
最后输出res即是总的方案;记得res要用long long;
假设每个位置都能切一刀,那么有Cn-1 ,2(我不知道排列组合怎么打)种方案
运用算法:前缀和
s[n] -s[n-1] = a[n];
所以当你从a[1]开始输入时,每次输入a[n]的时候都可以算出s[n],可以直接用s[n]表示a[n],然后输入单个元素的值之后直接加上前面的s[n-1]就变成n个数的和
完整代码如下:
#include <iostream>
using namespace std;
const int N = 1e5+10;
int s[N],cnt;
typedef long long LL;
int main()
{
int n;
cin >> n;
LL res;
for (int i = 1;i<= n;i++)
{
cin >> s[i];
s[i] += s[i-1];
}
if (s[n]%3)
{
puts("0");
return 0;
}
for (int i = 3;i<=n;i++)
// 因为有两刀,即有两个指针,各占一个位置,且不能等于n,所以i 从3开始,第一个指针为i-2,第二个为i-1
{
if(s[i-2] == s[n]/3) cnt++;
if(s[n]-s[i-1] == s[n]/3) res += cnt;
}
cout << res;
return 0;
}