Acwing 蓝桥杯集训.每日一题 截断数组

文章讲述了如何通过前缀和和递增策略,判断并计算一个数组能否被分成三个和相等的部分,以及确定切割位置的方法。关键在于利用前缀和计算和的变化,找出符合条件的切割点的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本题需要把一个数组截断成三个非空部分,并且每个部分内之和要相等,即每部分之和等于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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值