快乐子数组AcWing2023每日一题【前缀和,单调栈】

我们将 F(B,L,R) 定义为整数数组 BB 的索引从 L 到 R(包括两者)的子数组的各个元素之和。

更具体的说,F(B,L,R)=BL+BL+1+…+BR。

如果一个长度为 K 的整数数组 C 满足其所有前缀和均为非负整数,则称数组 C为快乐数组。

更具体的说,如果 F(C,1,1),F(C,1,2),…,F(C,1,K) 均为非负整数,则数组 CC为快乐数组。

给定一个包含 N 个整数的数组 A,请你计算数组 A 中的所有快乐连续子数组的元素和相加的结果。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含整数 N。

第二行包含 N 个整数 A1,A2,…,AN。

输出格式

每组数据输出一个结果,每个结果占一行。

结果表示为 Case #x: y,其中 x 为组别编号(从 1 开始),y 为所有快乐连续子数组的元素和相加的结果。

数据范围

1≤T≤100,
−800≤Ai≤800,
每个测试点最多 30 组数据满足 1≤N≤4×105,其余数据满足 1≤N≤200。

输入样例:
2
5
1 -2 3 -2 4
3
1 0 3
输出样例:
Case #1: 14
Case #2: 12
样例解释

在 Case 1 中,满足条件的快乐连续子数组有 [1],[3],[3,−2],[3,−2,4],[4],它们的元素和分别为 1,3,1,5,4,相加得到结果 14。

在 Case 2 中,满足条件的快乐连续子数组有 [1],[1,0],[1,0,3],[0],[0,3],[3],它们的元素和分别为 1,1,4,0,3,3,相加得到结果 12。


要使区间[i+1,j-1]的前缀和A[j-1]-A[i]>=0,就要让A[j-1]>=A[i];
找第一个A[j]<A[i]的下标j,那么区间任意一个以i+1为左端点,区间[i+1,j-1]内的点为右端点
的区间和都大于等于零;
即[i+1,j-1]这个区间内的快乐子数组有:
[i+1,i+1]
[i+1,i+2]
[i+2,i+3]
...
[i+1,j-1];
这些快乐连续子数组的元素和相加的结果为:
(1)w[i+1]*[j-(i+1)]+w[i+2]*[j-(i+2)]+...+w[j-1]*[j-(j-1)];

j-(i+1)、j-(i+2)...j-(j-1)的公差为-1,构造一个公差为-1的等差数列(n-1)、(n-2)...0;
B[n]=w[1]*(n-1)+w[2]*(n-2)+w[3]*(n-3)+...+w[n](n-n);
   B[i]=w[1]*(n-1)+w[2]*(n-2)+...+w[i]*(n-i);
   B[j-1]=w[1]*(n-1)+w[2]*(n-2)+...+w[i]*(n-i)+...+w[j-1]*[n-(j-1)];
(2)B[j-1]-B[i]=w[i+1]*[n-(i+1)]+w[i+2]*[n-(i+2)]+...+w[j-1]*[n-(j-1)];
(2)-(1):w[i+1]*(n-j)+w[i+2]*(n-j)+...+w[j-1]*(n-j),即(A[j-1]-A[i])*(n-j);

(1)=(2)-[(2)-(1)]=B[j-1]-B[i]-(A[j-1]-A[i])*(n-j);


#include<iostream>
using namespace std;
const int N=4e5+10,INF=0x3f3f3f3f;
typedef long long LL;
LL A[N],B[N];
LL stk[N];
int w[N];
int main()
{
    int T;
    scanf("%d",&T);
    for(int k=1;k<=T;k++)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
            A[i]=A[i-1]+w[i];//前缀和数列
            B[i]=B[i-1]+w[i]*(n-i);//B数列
        }
        LL res=0;
        int top=0;
        //A[n+1]=-INF;
        //stk[++top]=n+1;
        int j;
        for(int i=n;i>=0;i--)//0结束
        {
            while(top&&A[stk[top]]>=A[i]) top--;
            if(top)j=stk[top];
            else j=n+1;
            res+=B[j-1]-B[i]-(A[j-1]-A[i])*(n-j);//结果
            stk[++top]=i;
        }
        printf("Case #%d: %lld\n",k,res);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值