P12708 [KOI 2021 Round 1] 分割
题解描述
该题目要求将给定整数序列分割成四个连续的非空部分,且每个部分的和相等。关键在于如何高效地统计所有满足条件的分割方式。
方法思路
-
总和检查:首先计算整个序列的总和。如果总和不能被4整除,则无法分割,直接返回0。
-
前缀和预处理:使用前缀和数组
pre记录到每个位置为止,可以形成第一个分割点的次数。即,如果从开始到当前位置的前缀和等于总和的四分之一,则该位置可以作为第一个分割点。 -
后缀和预处理:同样,使用后缀和数组
suf记录从末尾到当前位置的和等于总和的四分之一的次数,用于标识第三个分割点后的部分。 -
中间分割点统计:遍历所有可能作为第二个分割点的位置,检查前半部分的和是否等于总和的一半。如果是,则将该位置的前缀计数和后缀计数相乘,累加到结果中,得到所有可能的分割方式。
pre[i]表示:切割在i和i+1之间作为第一个分割点的可能性数量
suf[i]表示:切割在i和i+1之间作为第三个分割点的可能性数量A1 A2 ... Ai | Ai+1 ... Aj | Aj+1 ... Ak | Ak+1 ... AN ↑ ↑ ↑ ↑ ↑ ↑ ↑ 第一个分割点 第二个分割点 第三个分割点 第四个分割点(固定为N)
-
第一个分割点:在位置i(切割在i和i+1之间)
-
第二个分割点:在位置j(切割在j和j+1之间)
-
第三个分割点:在位置k(切割在k和k+1之间)
-
解决代码
#include<bits/stdc++.h> #define ll long long using namespace std; const int N = 2e5 + 10,inf = 0x3f3f3f3f; ll s[N],a[N],n; // s数组是前缀和数组,a数组存储原始数据,n是数据个数 ll pre[N],suf[N]; // pre数组记录前i个位置中能作为第一个分割点的次数,suf数组记录后i个位置中能作为第三个分割点的次数 ll ans; // 存储最终答案 int main() { cin >> n; // 输入数据个数n for(int i = 1; i <= n; i++) cin >> a[i],s[i] = s[i - 1] + a[i]; // 输入数据并计算前缀和 if(s[n] % 4) { // 如果总和不能被4整除,直接输出0 cout << 0; return 0; } ll sum = 0; // 临时变量,用于累计和 for(int i = 2; i <= n; i++) // 从第二个位置开始遍历到最后一个位置 { sum += a[i - 1]; // 累计到前一个位置的和 if(sum == s[n] / 4) pre[i] = pre[i - 1] + 1; // 如果累计和等于总和的1/4,则当前位置可以作为第一个分割点,pre[i]增加1 else pre[i] = pre[i - 1]; // 否则保持前一个值 } sum = 0; // 重置临时变量 for(int i = n - 1; i >= 1; i--) // 从倒数第二个位置遍历到第一个位置 { sum += a[i + 1]; // 累计到后一个位置的和 if(sum == s[n] / 4) suf[i] = suf[i + 1] + 1; // 如果累计和等于总和的1/4,则当前位置可以作为第三个分割点,suf[i]增加1 else suf[i] = suf[i + 1]; // 否则保持后一个值 } sum = 0; // 再次重置临时变量 for(int i = 1; i <= n - 1; i++) // 遍历所有可能作为第二个分割点的位置 { sum += a[i]; // 累计到当前位置的和 if(sum == s[n] / 2) ans += pre[i] * suf[i + 1]; // 如果累计和等于总和的1/2,则前半部分可以分成两个等和部分,此时将pre[i]和suf[i+1]的乘积加到答案中 } cout << ans; // 输出答案 return 0; }

浙公网安备 33010602011771号