快乐子数组(二重前缀和+单调栈)

 题解:

根据题目 如果前缀和为非负数那么就是快乐子数组,我们要求的就是每组数据的所有快乐子数组的和。

我们可以预处理前缀和A[i]

假设j是快乐子数组的右端点 i是左端点 那么A[j]-A[i-1]>=0;

那么 A[j]>=A[i-1];

这里临界条件就是:找到第一个小于A[i]的A[j]这个数

设A为前缀和, 即找到第一个比A[i]小的数A[k], 这样从i+1到k-1之间的数就是一个快乐连续子数组

所以每个数都会被使用j-(下标)次 例如 w[i+1]会被使用(j-(i+1))次

这里可以列出一个式子:w[i+1](j-(i+1))+w[i+2](j-(i+2))....+w[j-1](j-(j-1)) 这个地方边界就是j-1所以到j-(j-1)就结束

可以看出来这个式子就是一个前缀和x等差数列 (等差为1)

这里我们可以预处理一下

B[i]=W[1](n-1)+......+W[n](n-n);(处理成乘等差为1的等差数列的前缀和);

用B[i]和A[i]来凑出上面的式子

B[j-1]-B[i]=W[i+1](n-(i+1))+......W[j-1](n-(j-1));

第一个式子减去上面这个式子得到:相差(A[j-1]-A[i])*(n-j);

最后!!!

得出最终式子!!!

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

#include<bits/stdc++.h>
using namespace std;
const int N=400010,INF=1e9;
typedef long long LL;
LL A[N],B[N],stk[N];
int main(){
    int t;
    scanf("%d",&t);
    for(int Case=1;Case<=t;Case++)
    {
        int n;
        scanf("%d", &n);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            A[i]=A[i-1]+x;
            B[i]=B[i-1]+x*(n-i);
        }
        LL res = 0;
        int tt = 0;
        A[n + 1] = -INF;
        stk[ ++ tt] = n + 1;
        for (int i = n; i >= 0; i -- )
        {
            while (A[stk[tt]] >= A[i]) tt -- ;
            int j = stk[tt];
            stk[ ++ tt] = i;
            res += B[j - 1] - B[i] - (A[j - 1] - A[i]) * (n - j);
        }
        printf("Case #%d: %lld\n",Case,res);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值