首页 > 试题广场 >

喜欢切数组的红

[编程题]喜欢切数组的红
  • 热度指数:9200 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 512M,其他语言1024M
  • 算法知识视频讲解
\hspace{15pt}小红有一个长度为 n 的数组 \{a_1,a_2,\dots,a_n\} ,她打算将数组切两刀变成三个非空子数组,使得每一个子数组中至少存在一个正数,且每个子数组的和都相等。
\hspace{15pt} 看起来不是很难,所以小红想让你求解,一共有多少种不同的切分方案。

输入描述:
\hspace{15pt}第一行输入两个整数 n \left( 3 \leqq n \leqq 2 \times 10^5 \right) 代表数组中的元素数量。
\hspace{15pt}第二行输入 n 个整数 a_1,a_2,\dots,a_n \left( -10^9 \leqq a_i \leqq 10^9 \right) 代表数组元素。


输出描述:
\hspace{15pt}在一行上输出一个整数,代表切分方案数。
示例1

输入

3
3 3 3

输出

1
示例2

输入

6
1 1 4 5 1 4

输出

0
示例3

输入

10
0 3 4 2 3 2 1 -1 3 4

输出

2
只要在外层循环加一个条件,就可以排除非常多情况!因为三个区间和相等,所以每个区间的和就是总和的1/3!!!可以联立sums[cut2-1]-sums[cut1-1]=sums[cut1-1]和sums[n-1]-sums[cut2-1]=sums[cut1-1]相等的条件,化简方程组得到sums[n-1]=3*sums[cut1-1]因此在判断cut1位置是否满足有一个数大于0条件后,并上化简得到的等式,就可以在外层循环排除掉非常多的情况,节约大量时间。内层循环再做一次相等判断即可。
#include <stdio.h>
#define MAXN 200000
void calcsums(long a[], long s[], long c[], long n);

void calcsums(long a[], long s[], long c[], long n){
    long i=0;
    s[0]=a[0];
    if(a[i]>0)c[i]++;
    for(i=1;i<=n;i++){
        s[i]=s[i-1]+a[i];
        c[i]=c[i-1];
        if(a[i]>0)c[i]++;
    }
}
int main() {
    long n;
    scanf("%ld",&n);
    long i;
    long ar[MAXN];
    for(i=0;i<n;i++){
        scanf("%ld",&ar[i]);
    }
    long sums[MAXN]={0};
    long checker[MAXN]={0};
    calcsums(ar,sums,checker,n);

    //chc(ar,checker,n);
    long cut1,cut2; //end1 end2
    long s1,s2,s3;
    long m=0;
    for(cut1=1;cut1<n-1;cut1++){
        if(checker[cut1-1]>0&&3*sums[cut1-1]==sums[n-1]){
            s1=sums[cut1-1];
            for(cut2=cut1+1;cut2<n;cut2++){
                if(checker[n-1]-checker[cut2-1]>0&&checker[cut2-1]-checker[cut1-1]>0){
                    
                    s2=sums[cut2-1]-sums[cut1-1];
                    if(s1==s2){
                        m+=1;
                    }
                }
            }
        }
    }
    printf("%ld",m);
    return 0;
}


发表于 2025-05-01 21:31:38 回复(0)
我的方法是因为三个区间和相等,所以每个区间和是总和的1/3,然后因为它是切三份,
所以确定两刀的位置就可以,从头开始遍历头区间,记录一下符合条件的头区间位置
索引,从尾开始也这样遍历一遍,记录符合条件的尾区间索引,这样头尾两边就确定好了,
最后再看下这两个位置的中间的区间是否符合条件即可。但是最后一个1w数据的测试用例没过
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        int nums = in.nextInt();
        int array[] = new int[nums];
        int sum = 0;
        for (int i = 0; i < nums; i++) {
            array[i] = in.nextInt();
            sum = sum + array[i];
        }
        int target = sum / 3;
        int sumOfPart = 0;
        ArrayList<Integer> indexLeftList = new ArrayList<>();
        ArrayList<Integer> indexRightList = new ArrayList<>();
        boolean positive = false;;
        for (int i = 0; i < array.length; i++) {
            if (array[i] > 0) {
                positive = true;
            }
            sumOfPart += array[i];
            if (sumOfPart == target && positive) {
                indexLeftList.add(i);
            }
        }
        sumOfPart = 0;
        positive = false;
        for (int i = array.length - 1; i >= 0; i--) {
            if (array[i] > 0) {
                positive = true;
            }
            sumOfPart += array[i];
            if (sumOfPart == target && positive) {
                indexRightList.add(i);
            }
        }
        int count = 0;
        for(int i = 0;i<indexLeftList.size();i++){
            for(int j = j=0;j<indexRightList.size();j++){
                if(indexLeftList.get(i)<indexRightList.get(j)){
                    if(isValid(indexLeftList.get(i),indexRightList.get(j),array,target)){
                        count++;
                    }
                }
            }
        }
        System.out.print(count);
    }
    public static boolean isValid(int left,int right,int[]nums,int target){
        int sum = 0;
        boolean positive = false;
        for(int i = left+1;i<=right-1;i++){
            if (nums[i] > 0) {
                positive = true;
            }
            sum+=nums[i];
        }
        if(sum==target && positive){
            return true;
        }else{
            return false;
        }
    }

}

发表于 2025-07-27 22:42:18 回复(0)
参考题解中大佬的解法,时间复杂度是O(n)
f中存放的是包括i在内的右侧可以构成的第三部分的数量,从数组右开始遍历,初始值是0,
next很有意思,保存的是i开始向右的第一个正数位置,目的待会再说
最后从左开始遍历,每次遇到可以作为最后一个元素构成第一部分的i,那么从i开始到第一个正元素,因为第二部分需要正数,那么只要看,从这个第一部分右边的第一个正数(第二部分必须)再右边能构成第三部分的数量,就是这个第一部分以i为结束能完成题目要求的构成方法次数,然后累加就完了,能构成第一部分,然后中间算了一个正数,然后去除第三部分,中间的第一个正数加上剩下的元素一定是合法第二部分
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
    // Write your code here
    let n = Number(await readline());
    let a = (await readline()).split(" ").map(Number);
    let sum = [a[0]];
    let k = [];
    let f = new Array(n + 1).fill(0);
    let next = new Array(n + 1).fill(0);
    k.push(a[0] > 0 ? 1 : 0);
    for (let i = 1; i < n; i++) {
        sum.push(a[i] + sum[i - 1]);
        k.push(a[i] > 0 ? k[k.length - 1] + 1 : k[k.length - 1]);
    }
    function fun() {
        let r = 0;
        if (sum[n - 1] % 3) {
            return r;
        }
        next[n] = n;
        for (let i = n - 1; i; i--) {
            f[i] =
                k[n - 1] - k[i - 1] > 0 &&
                sum[n - 1] - sum[i - 1] === sum[n - 1] / 3
                    ? f[i + 1] + 1
                    : f[i + 1];
            next[i] = a[i] > 0 ? i : next[i + 1];
        }
        for (let i = 0; i < n - 2; i++) {
            if (k[i] && sum[i] === sum[n - 1] / 3 && next[i + 1] < n - 1) {
                r += f[next[i + 1] + 1];
            }
        }
        // console.log(f);

        // console.log(next);
        return r;
    }
    console.log(fun());
    // console.log(k);
})();

发表于 2025-06-26 00:15:01 回复(0)
import java.util.*;

/*

    暴力穷举
   
    两个条件:
    1.子数组至少存在一个正数
    2.每个子数组和都相等

    先使用前缀和找出所有的和相等的情况,然后在该情况对是否所有子数组至少存在一个正数做判断

*/


public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int[] nums = new int[n];
        //前缀和,代表当前索引即之前的所有元素的和
        int[] pre = new int[n];
        //统计正数个数
        int[] pos = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        pre[0] = nums[0];
        if (nums[0] > 0) {
            pos[0] = 1;

        } else {
            pos[0] = 0;
        }
        for (int i = 1; i < n; i++) {
            pre[i] = nums[i] + pre[i - 1];
            if (nums[i] > 0) {
                pos[i] = pos[i - 1] + 1;
            } else {
                pos[i] = pos[i - 1];
            }
        }
        // for (int i = 0; i < n; i++) {
        //     // pre[i] = nums[i] + pre[i - 1];
        //     System.out.println(pre[i]);
        // }

        //控制两个下标检索
        //计算每个子数组累计和

        /*
            假设i是第一刀,j是第二刀
            sum1=pre[i]
            sum2=pre[j]-pre[i];
            sum3=pre[n-1]-pre[j];
            避免遍历

            正数判断复杂度太高
            剪枝
            先判断正数

        */
        int count = 0;
        // boolean first = false;
        //第一刀从1-倒数第三个
        for (int i = 0; i <= n - 3; i++) {

            // if (nums[i] > 0) first = true;
            if (pos[i] <= 0) continue;
            int sum1 = pre[i];
            if (sum1 != pre[n - 1] / 3) continue;
            // boolean second = false;
            //第二刀从i+1-倒数第二个
            for (int j = i + 1; j <= n - 2; j++) {

                // if (nums[j] > 0) second = true;
                if (pos[j] - pos[i] <= 0) continue;
                if (pos[n - 1] - pos[j] <= 0) continue;

                int sum2 = pre[j] - pre[i];
                // if (sum1 != sum2) continue;
                int sum3 = pre[n - 1] - pre[j];

                //满足条件2
                if (sum1 == sum2 && sum2 == sum3) {

                    count++;

                }
            }

        }

        System.out.println(count);

    }

}
n^2的暴力求解+剪枝
本来是前缀和求子数组值,先判断子数组和都相等,再判断包含正数,超时;
后面先判断包含正数再判断和相等,超时;
思考了一下包含正数也可以用前缀和(本来用遍历),超时;
发现在外层循环就可以获取到第一部分前缀和,通过判断该前缀和是否为原数组值三分之一(本来都没想到,看到讨论里有个大佬在说这个)剪枝,通过
发表于 2025-04-13 19:10:33 回复(0)
//隔壁兄弟代码的复杂度是O(n*n) 我的代码复杂度是O(n log n) 即遍历所有jlist的值 但是ilist的每个值只执行一次  大大缩减 俩种都是对的 只是用例数据量太少了 当数据量超过2e5次方后(2乘以10的5次方) 复杂度是O(n*n)会超时  PS:D老师的线段树实在是看不懂 但是逻辑是一样的  大体思路是写3个数组 分别判断第一段 第二段 第三段数据有正数   先筛选符合条件的i j 分别组成ilist 和 jlist列表存放 条件是num[i]=总和/3 num[j]=总和*2/3  并且对应的判断数组要true
再就是遍历和组合不同的i j 找出符合的(i,j)对 计算数量 当j第一个元素a满足了ilist列表中的部分 j取第二个元素的时候 之前a满足的 b就不用在判断了 直接通过 即数量+1 判断比a大但是比b小的i元素即可 (PS 这里有个坑 a之前可以没有正数,但【a,b】区间的可能有正数 这些数量要加上) 最后计算总数量 out


import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] s = new int[n + 1]; //原始数据
        long[] num = new long[n + 1];
        long tol = 0;
        for (int i = 1; i < n + 1; i++) {
            s[i] = in.nextInt();
            tol = tol + s[i];
            num[i] = num[i - 1] + s[i];
        }

        if (tol % 3 != 0) {
            System.out.println(0);
            return;
        }
        long p = tol / 3;
        //构造第一组数据有正数的判断
        boolean[] first = new boolean[n + 1]; //第一段
        first[0] = false;
        for (int i = 1; i < n + 1; i++) {
            first[i] = first[i - 1] || (s[i] > 0);
        }
        int[] second = new int[n + 1];
        second[n] = n + 1;
        for (int i = n - 1; i >= 0; i--) {
            if (s[i] > 0) {
                second[i] = i;
            } else {
                second[i] = second[i + 1];
            }
        }
        boolean[] third = new boolean[n + 1]; //记录每个位置之后是否存在正数
        third[n] = false;
        for (int i = n - 1; i >= 0; i--) {
            third[i] = (s[i + 1] > 0) || third[i + 1];
        }
        List<Integerilist = new ArrayList<>();
        for (int i = 1; i < n + 1; i++) {
            if (num[i] == p && first[i]) {
                ilist.add(i);
            }
        }
        List<Integerjlist = new ArrayList<>();
        for (int i = 1; i < n + 1; i++) {
            if (num[i] == 2 * p && third[i]) {
                jlist.add(i);
            }
        }
        Collections.sort(ilist);
        Collections.sort(jlist);
        int z = 0;//ilist里的元素序号
        List<Integertest = new ArrayList<>();
        int temp = 0;
        int previoustemp = 0;
        int preresult = 0;
        for (int j : jlist) { //满足条件i<j 并且second[i]<j 第二段要有正数
            int result = 0;
            if(previoustemp<=j){
                result =z;
            }else{
                result = preresult;
            }
            while (z < ilist.size() && ilist.get(z) < j) {
                temp = second[ilist.get(z)];
                if (temp <= j) {
                    result++;
                }
                z++;
            }
            previoustemp = temp;
            preresult = result;
            test.add(result);
        }
        int ans = 0;
        for (int k = 0; k < test.size(); k++) {
            ans = ans + test.get(k);
        }
        System.out.println(ans);
    }
}
发表于 2025-03-30 15:17:11 回复(0)
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int a = in.nextInt();
        //原始数组
        int[] num = new int[a];
        //记录正整数的个数,累计的
        int[] zs = new int[a];
        //记录每个索引位置求和
        int[] sum = new int[a];
        int z=0;
        int s=0;
        for(int i=0;i<a;i++){
            int b = in.nextInt();
            num[i]=b;
            if(b>0){
                z++;
            }
            zs[i]=z;
            s+=b;  
            sum[i]=s;
        }
        int count=0;
        int target = sum[a-1]/3;
        for(int i=0;i<a-2;i++){
            if(zs[i]==0 || sum[i]!=target){
                continue;
            }
            if(zs[a-1]-zs[i]==0){
                    break;
            }
            for(int j=i+1;j<a-1;j++){
                if(zs[j]-zs[i]==0){
                    continue;
                }
                if(zs[a-1]-zs[j]==0){
                    break;
                }
                if(sum[j]-sum[i]==target){
                        count++;
                }

            }
        }
        System.out.print(count);
    }
}
发表于 2025-03-28 17:43:56 回复(0)
10
0 3 4 2 3 2 1 -1 3 4
这个案例能有2个方案?不是切两刀3个数组吗,还要数字和相等,只能从左往右切啊,顺序还能打乱的?
发表于 2025-03-15 09:21:06 回复(4)
这题和动态规划有啥关系啊,害我想了半天
发表于 2025-12-08 00:16:35 回复(0)
测试集中有规模超过10000的数据,因此算法的时间复杂度不能达到或超过O(n^2),贴一个自己的算法
while 1:
    try:
        n = int(input())
        ls = list(map(int,input().split()))
        if sum(ls)%3 != 0:
            print(0)
            break
        ans1 = sum(ls)//3
        ans2 = ans1*2
        sumls = [0]
        pls = [0]
        for i in range(n):
            sumls.append(sumls[-1]+ls[i])
            if ls[i]>0:
                pls.append(pls[-1]+1)
            else:
                pls.append(pls[-1])
        sumls = sumls[1:]
        pls = pls[1:]
        count = 0
        ans1ind = []
        ans2ind = []
        for i in range(n):
            if sumls[i]==ans1 and pls[i]>0:
                ans1ind.append(i)
            elif sumls[i]==ans2 and pls[-1]-pls[i]>0:
                ans2ind.append(i)
        for x in ans1ind:
            for y in ans2ind:
                if x<y and pls[y]-pls[x]>0:
                        count += 1
        print(count)
        break
    
    except:
        break


发表于 2025-10-08 20:06:35 回复(0)
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    while(cin>>n){
        vector<long> num(n);
        vector<vector<long long>> sum(n,vector<long long>(2,0));
        long long s=0 ,count=0;
        for(int i=0;i<n;i++){
            cin>>num[i];
            s+=num[i];
            sum[i][0]=s;//统计前缀和
            if(num[i]>0)count++;
            sum[i][1]=count;//,每个前缀和对应的正数个数
        }
        //for(int i=0;i<n;i++)cout<<sum[i][0]<<" "; cout<<endl;
        if(sum[n-1][0]%3!=0||sum[n-1][1]<3){//剔除不是3的倍数,以及正数小于3的
            cout<<0<<endl;
        }else{
            int sm=0;
            long long tmp=sum[n-1][0]/3;
            vector<int> tmppos1;//放第一段切分的下标
            vector<int> tmppos2;//放第二段切分的下标
            for(int i=0;i<n;i++){//统计前两段下标
                if(tmp==sum[i][0]) {
                    tmppos1.push_back(i);
                }
                if(2*tmp==sum[i][0]){
                    tmppos2.push_back(i);
                }
            }
            for(int i=0;i<tmppos1.size();i++){//根据每个字段正数至少一个暴力求解
                for(int j=0;j<tmppos2.size();j++){
                    if(sum[tmppos1[i]][1]>=1){
                        if((sum[tmppos2[j]][1]-sum[tmppos1[i]][1]>=1)&&
                        (sum[n-1][1]-sum[tmppos2[j]][1]>=1)){
                            sm++;
                        }
                    }
                }
            }
            cout<<sm<<endl;
        }
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

发表于 2025-10-06 23:59:57 回复(0)
#include <stdio.h>

int main() {
    int n = 0;
    scanf("%d", &n);
    int a[200000] = {0};
    int sum = 0;
    int s[200000] = {0};//前缀和
    int d1[200000] = {0};//1/3位置
    int d2[200000] = {0};//2/3位置
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        sum += a[i];
        s[i] = sum;
    }
    int result  = 0;
    if (sum != 0) {
        if (sum % 3 == 0) {
            int cnt1 = 0;
            int cnt2 = 0;

            for (int i = 0; i < n ; i++) {
                if (s[i] == sum / 3) {
                    int flag = 0;//前1/3有正数
                    for (int j = 0; j <= i; j++) {
                        if (a[j] > 0) {
                            flag = 1;
                            break;
                        }
                    }
                    if (flag) {
                        d1[cnt1++] = i;
                        flag = 0;
                    }

                }
            }
            for (int i = n - 1; i >= 0 ; i--) {
                if (s[i] == 2 * sum / 3) {
                    int flag = 0;//后1/3有正数
                    for (int j = n - 1; j >= i; j--) {
                        if (a[j] > 0) {
                            flag = 1;
                            break;
                        }
                    }
                    if (flag) {
                        d2[cnt2++] = i;
                        flag = 0;
                    }
                }
            }

            //结果
            for (int i = 0; i < cnt1; i++) {
                for (int j = 0; j < cnt2; j++) {
                    if (d2[j] > d1[i]) {
                        //中间1/3有正数
                        int flag = 0;
                        for (int k = d1[i]; k <= d2[j]; k++) {
                            if (a[k] > 0) {
                                flag = 1;
                                break;
                            }
                        }
                        if (flag) {
                            result++;
                            flag = 0;
                        }
                    }
                }
            }
        }
    } 
    // else if (sum == 0) {
    //     int cnt = 0;
    //     for (int i = 0; i < n ; i++) {
    //         if (s[i] == 0) cnt++;
    //     }
    //     cnt--;
    //     result = cnt * (cnt - 1) / 2;
    // }
    printf("%d", result);
    return 0;
}



发表于 2025-09-28 21:37:15 回复(0)
看了一下大家写的题解,感觉我的是最容易理解的
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    long long sum = 0;

    vector<long long> nums(n);
    vector<long long> presum(n+1);//前i个数字的前缀和
    vector<int> dp(n+1);//前i个数字有几个正数
    for (int i = 0; i<n; ++i)
    {
        cin >> nums[i];
        sum += nums[i];
    }
    
    //加工前缀和数组,正数统计数组
    
    for (int i = 1; i<=n; ++i)
    {
        presum[i] = presum[i-1]+nums[i-1];

        //真几把无语,这还非得加个括号,我找半天错误,原来是这
        dp[i] = dp[i-1] + (nums[i-1] > 0 ? 1 : 0);   
    }
    if (sum%3 != 0 || dp[n] == 0)
    {
        cout << 0;
        return 0;
    }

    long long target = sum/3;
    int ans = 0;
    for (int i = 1; i<=n; ++i)
    {
        //前i个数字累加和为1/3的sum,且有正数
        if (presum[i] == target && dp[i] > 0)
        {
            
            for (int j = i+1; j<=n; ++j)
            {
                //前j个数字累加和为2/3的sum,且每段都有正数
                if (presum[j] == 2*target && dp[j] - dp[i] > 0 && dp[n] - dp[j] > 0)
                {
                    ans++;
                }
            }
        }
    }
    cout << ans;
}
// 64 位输出请用 printf("%lld")

发表于 2025-09-08 18:52:22 回复(1)
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> presum(n + 1, 0);
    vector<int> poscount(n + 1, 0);
    for (int i = 0; i < n; i++) {
        int a;
        cin >> a;
        presum[i + 1] = presum[i] + a;
        poscount[i + 1] = poscount[i] + (a > 0 ? 1 : 0);
    }
    int sumtotal = presum[n];
    if (sumtotal % 3 != 0 ) {
        cout << 0 << endl;
        return 0;
    }
    int sum = sumtotal / 3;
    int count = 0, result = 0;
    for (int i = 1; i <= n - 2; i++) {
        int suma = presum[i];
        if (sum != suma || poscount[i] <= 0)
            continue;
        for (int j = i + 1; j <= n - 1; j++) {
            int sumb = presum[j] - presum[i];
            int numb = poscount[j] - poscount[i];
            if (sum != sumb || numb <= 0)
                continue;

            int sumc = sumtotal - presum[j];
            int numc = poscount[n] - poscount[j];
            if (sumc == sum && numc > 0)
                result++;
        }

    }
    cout << result << endl;
    return 0;
}

发表于 2025-08-28 23:13:52 回复(0)
我用的时间复杂度较高,但是这题能过;大致思路是先找到一个和能为sum/3的值,
再去搜索以这个节点为第一刀能砍第二刀的位置,然后回溯,找到下一个第一刀能在的位置
以此为基点去找第二刀,直到遍历完所有可能的第一刀
import java.util.Scanner;

import java.util.ArrayList;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n=in.nextInt();
        int sums=0;
        int a[]=new int[n];
        for(int i=0;i<n;++i){
            a[i]=in.nextInt();
            sums+=a[i];
        }
        if(sums%3!=0){
            System.out.println(0);
        }else{
            int tempsum=sums/3;
        jrun(a,new ArrayList<Integer>(),0,tempsum);
        System.out.println(Acount);
        }
    }
    static int Acount=0;
    private static void jrun(int []an,ArrayList<Integer> path,int begin,int sum){
        int sumnow=0;
        if(path.size()==2){
            Acount++;
            return;
        }
        boolean flag=false;
        for(int i=begin;i<an.length;++i){  
            if(an[i]>0){flag=true;}
            sumnow+=an[i];
            if(sumnow==sum&&flag){
                ArrayList<Integer> path1=new ArrayList<Integer>(path);
                path1.add(i);
                jrun(an,path1,i+1,sum);
            }
        }


    }
}

发表于 2025-08-08 22:16:41 回复(0)
n = int(input())
a = list(map(int, input().split()))
total = sum(a)

if total % 3 != 0:
    print(0)
    exit()

target = total // 3

# Step 1: 后缀标记(判断是否可以作为合法“第三段”)
suf = [0] * n
suffix_sum = 0
has_pos = False
for i in range(n-1, -1, -1):
    suffix_sum += a[i]
    if a[i] > 0:
        has_pos = True
    if suffix_sum == target and has_pos:
        suf[i] = 1

# Step 2: 后缀和数组,suf[i] 表示 i 到末尾有多少合法“第三段”起点
for i in range(n-2, -1, -1):
    suf[i] += suf[i+1]

# Step 3: 正向处理第一段,同时跟踪中间段是否含正数
cnt = 0
prefix_sum = 0
mid_sum = 0
has_prefix_pos = False
has_mid_pos = False

for i in range(n-2):  # 第一刀最多切到 n-3
    prefix_sum += a[i]
    if a[i] > 0:
        has_prefix_pos = True
    if prefix_sum == target and has_prefix_pos:
        # 初始化中段判断
        mid_sum = 0
        has_mid_pos = False
        # 从 i+1 扫一遍作为中间段,尝试找第二刀位置
        for j in range(i+1, n-1):
            mid_sum += a[j]
            if a[j] > 0:
                has_mid_pos = True
            if mid_sum == target and has_mid_pos:
                # 第三段必须从 j+1 开始
                cnt += suf[j+1]
                break  # 只要找到第一个合法中间段就可以,避免重复计算
print(cnt)

发表于 2025-07-18 00:42:21 回复(0)
public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int m = in.nextInt();
        in.nextLine();
        int[] listm = new int[m];
        for (int i = 0; i < m; i++) {
            listm[i]=in.nextInt();
        }
        int num=0;
        for(int i=1;i<m-1;i++) {
            for(int j=i+1;j<m;j++) {
                List<Integer> l1=Arrays.stream(Arrays.copyOfRange(listm,0,i)).boxed().collect(Collectors.toList());
                Optional<Integer> m1=l1.stream().max(Integer::compare);
                Optional<Integer> s1=l1.stream().reduce(Integer::sum);
                List<Integer> l2=Arrays.stream(Arrays.copyOfRange(listm,i,j)).boxed().collect(Collectors.toList());
                Optional<Integer> m2=l2.stream().max(Integer::compare);
                Optional<Integer> s2=l2.stream().reduce(Integer::sum);
                List<Integer> l3=Arrays.stream(Arrays.copyOfRange(listm,j,m)).boxed().collect(Collectors.toList());
                Optional<Integer> m3=l3.stream().max(Integer::compare);
                Optional<Integer> s3=l3.stream().reduce(Integer::sum);
                if(m1.get()>0 && m2.get()>0 && m3.get()>0) {
                    if(s1.get()==s2.get() && s2.get()==s3.get()) {
                        num++;
                    }
                }
            }
        }
        System.out.println(num);
    }
发表于 2025-06-07 17:34:36 回复(0)
def solve():
    n = int(input().strip())
    items = [int(i) for i in input().strip().split(" ")]
    isum = sum(items)
    if isum % 3 != 0:
        print(0)
        return
    isum3 = isum // 3

    # 找到两侧第一个正数下标
    # 实际测试时不管l,r也过了,就很怪
    l = 0
    while l < n:
        if items[l] > 0:
            break
        l += 1

    r = n - 1
    while r >= 0:
        if items[r] > 0:
            break
        r -= 1

    prefix_sum = [0] * (n + 1)
    n_13 = 0  # 当前左侧前缀和与isum3可用交点数量
    n_13_candidate = 0 # 还不可用的交点数量,得遇到一个正数才能加到上面去
    n_cuts = 0
    for i in range(r):
        prefix_sum[i] = prefix_sum[i - 1] + items[i]  # 0 - 1 = -1,对Python来说这个下标无妨
        if items[i] > 0:
            n_13 += n_13_candidate
            n_13_candidate = 0
        if prefix_sum[i] == isum3 and i >= l:
            n_13_candidate += 1
        if prefix_sum[i] == 2 * isum3:
            n_cuts += n_13
    print(n_cuts)


solve()

发表于 2025-04-30 17:50:40 回复(0)
//硬生生挤出来了
//1.输入时统计正数数量,如果<3 直接为0
//2. 统计第一个片段的和时,判断总和如果不是该片段3倍,直接跳过

#include <iostream>
#include <vector>
#include<bits/stdc++.h>
using namespace std;

bool isRight(vector<long> &vec,long left,long right)
{
bool flag1=false,flag2=false,flag3=false;
for(long i=0;i<vec.size();i++)
{
if(i<=left&&vec[i]>0){flag1=true;i=left;continue;}
if(i>left&&i<right&&vec[i]>0){flag2=true;i=right-1;continue;}
if(i>=right&&vec[i]>0){flag3=true;break;}
}
if(flag1&&flag2&&flag3)return true;
else return false;
}

int main() {
int n;cin>>n;
vector<long> vec(n);
long sum=0;
int numP=0;
for(int i=0;i<n;i++)
{cin>>vec[i];sum+=vec[i];
if(vec[i]>0)numP++;
}

vector<pair<long,long>> result;
long sum1=0;long sum2=0;
for(long i=0;i<n-1;i++)
{
sum1+=vec[i];
if(numP<3)break;
if(sum1!=0)
{
if(sum%sum1!=0||sum/sum1!=3)continue;
}

sum2=0;
for(long j=n-1;j>i;j--)
{
sum2+=vec[j];
if(sum1==sum2&&sum-sum1-sum2==sum2)
{
//函数判断是否正确:1. 三个片段含有正数
if(isRight(vec, i, j))
{
result.push_back(make_pair(i,j));
}

}

}
}
cout<<result.size();

}
// 64 位输出请用 printf("%lld")
发表于 2025-04-18 15:47:42 回复(0)
k = int(input())
nums = list(map(int, input().split()))


def count_solution(nums_list):
    sum_list = []
    positive_list = []
    sum_temp = 0
    for num in nums_list:
        if num > 0:
            positive_list.append(1)
        else:
            positive_list.append(0)
        sum_temp += num
        sum_list.append(sum_temp)
    first_son_sum = sum_list[-1] / 3
    # 寻找第一次分割
    frist_maybe_c = sum_list.count(first_son_sum)
    if frist_maybe_c == 0:
        return 0
    else:
        frist_maybe_index = []
        i = -1
        while len(frist_maybe_index) < frist_maybe_c:
            i = sum_list.index(first_son_sum, i + 1)
            frist_maybe_index.append(i)
        # 寻找第二次分割位置
        second_maybe_index = []
        second_son_sum = 2 * first_son_sum
        second_maybe_c = sum_list.count(second_son_sum)
        i = 0  # 第二次不可能从第一位开始分割
        while len(second_maybe_index) < second_maybe_c:
            i = sum_list.index(second_son_sum, i + 1)
            # 防止出现和为零时导致第二次分割的位置在末尾
            if i < len(nums_list):
                second_maybe_index.append(i)
        all_solotion = []
        for m in frist_maybe_index:
            for n in second_maybe_index:
                if m < n:  # 呃附着的出现可能会导致N大于M
                    # 保证三个数组都有正shu存在
                    if (
                        sum(positive_list[: m + 1]) > 0
                        and sum(positive_list[m : n + 1]) > 0
                        and sum(positive_list[n:]) > 0
                    ):
                        all_solotion.append([m, n])
    return len(all_solotion)


print(count_solution(nums))
#超时,通过率50%,就当给大家提供思路吧

发表于 2025-04-13 18:13:04 回复(0)
从左往右找一刀,从右往左找一刀。
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int n = Integer.parseInt(in.nextLine());
            int sum = 0;
            int[] arr = new int[n];
            for(int i = 0; i < n; i++){
                arr[i] = in.nextInt();
                sum += arr[i];
            }
            if(sum % 3 != 0){
                System.out.println(0);
                continue;
            }
            int avg3 = sum / 3;
            int sum1 = 0;//第一个子数组和
            int count = 0;
            int rightIndex = n - 1;
            int firstflag = 0;//第一个数组是否有正数
            int secondflag = 0;//第三个数组是否有正数
            for(int i = 0; i < rightIndex - 3; i++){
                firstflag = Math.max(firstflag,arr[i]); 
                sum1 += arr[i];
                //从左往右找到第一刀
                if(sum1 == avg3 && firstflag > 0){
                    int sum2 = 0;//第三个子数组和
                    //从右往左找到第二刀
                    secondflag = 0;
                    for(int j = rightIndex; j >= i+2; j--){
                        secondflag = Math.max(secondflag,arr[j]);
                        sum2 += arr[j];
                        if(sum2 == avg3 & secondflag > 0 && hasZhengShu(arr,i + 1,j)){
                            count++;
                        }
                    }
                }
            }
            System.out.println(count);
        }
    }

    //判断数组内是否有正数
    static boolean hasZhengShu(int[] arr, int i, int j){
        for(; i < j; i++){
            if(arr[i] > 0){
                return true;
            }
        }
        return false;
    }
}


发表于 2025-04-08 01:45:19 回复(0)