[Daimayuan] 三段式(C++,数组前缀和)

有一个长度为 n n n的序列,现在我们想把它切割成三段(每一段都是连续的),使得每一段的元素总和都相同,请问有多少种不同的切割方法

输入描述

第一行给出一个数 n n n,( 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105)

第二行给出序列 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3,…, a n a_n an,( ∣ a i ∣ ≤ 1 0 5 |a_i|≤10^5 ai105)

输出描述

输出一个数表示有多少种不同的切割方法

样例输入
4
1 2 3 3
样例输出
1
样例解释

可以将它分成第一组 1 1 1 2 2 2,第二组 3 3 3,第三组 3 3 3

解题思路

根据题意,很容易得到下面的公式:
s u m 1 + s u m 2 + s u m 3 = s u m s u m 1 = s u m 2 = s u m 3 s u m 1 = s u m 2 = s u m 3 = 1 3 s u m sum_1+sum_2+sum_3=sum\\ sum_1=sum_2=sum_3\\ \\ sum_1=sum_2=sum_3=\frac{1}{3}sum sum1+sum2+sum3=sumsum1=sum2=sum3sum1=sum2=sum3=31sum
要满足题意有两个前提条件(特殊判定):

(1) s u m   %   3 = = 0 sum\ \%\ 3==0 sum % 3==0

(2) n ≥ 3 n\ge3 n3

if (n < 3 || sum % 3 != 0) {
    cout << 0 << endl;
    return 0;
}

然后我们来寻找可能的分割方式。

如果有多于 1 1 1种的分割方法,那么一定存在子段和为 0 0 0

与其去找子段和为 0 0 0的部分,不如转化思维,使用前缀和的概念(经常用于连续区间和问题)。

这样我们就只需要寻找前缀和同为 1 3 s u m \frac13sum 31sum的部分了。

然后具体问题具体分析,因为题目中只要求分割为三段,所以我们直接找出前后两段 1 3 s u m \frac13sum 31sum即可。

注意,至少要为中间的 1 3 s u m \frac13sum 31sum预留一个元素的位置。

long long ans = 0;
for (int i = 1; i <= n - 1; i++) {
	if (pre[i] == subsum) {
		ans += tail_counter[i + 2];
	}
}

其中tail_counter中的元素意义为: i i i位置及其之后有多少个后缀和为 1 3 s u m \frac13sum 31sum

最后,AC代码如下:

#include <iostream>
using namespace std;
const int max_n = 1e5;

long long arr[max_n + 1], pre[max_n + 1], tail[max_n + 2];
long long tail_counter[max_n + 1];

int main() {
	int n;
	long long sum = 0;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> arr[i];
		sum += arr[i];
	}

	if (n < 3 || sum % 3 != 0) {
		cout << 0 << endl;
		return 0;
	}
	long long subsum = sum / 3;
	for (int i = 1; i <= n; i++) {
		pre[i] = arr[i] + pre[i - 1];
	}
	for (int i = n; i >= 1; i--) {
		tail[i] = arr[i] + tail[i + 1];
		tail_counter[i] = tail_counter[i + 1];
		if (tail[i] == subsum) tail_counter[i]++;
	}
	long long ans = 0;
	for (int i = 1; i <= n - 1; i++) {
		if (pre[i] == subsum) {
			ans += tail_counter[i + 2];
		}
	}
	cout << ans << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值