题解:
根据题目 如果前缀和为非负数那么就是快乐子数组,我们要求的就是每组数据的所有快乐子数组的和。
我们可以预处理前缀和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);
}
}