题目描述
Description
给定一个数串,数串的长度为 n ,现在将一个子串的每个数字之和定义为该子串的数串和,请你求出数串中有多少个子串的数串和为正数。
Input
第一行一个数 n ,表示数串的长度。第二行一共 n 个数,表示数串中的每个数输出就一个数,表示数串中有多少个子串的数串和为正数。
Output
一个正数即答案。
Sample Input 1
4
3 8 -9 2
Sample Output 1
3
Hint
对于100%的数据: n ≤ 100000,所有数之和在int范围之内
Time Limit
1000MS
Memory Limit
256MB
题意分析
首先要明确子串一定是连续的、紧挨着的序列不可以是单个元素!
这道题要求出子串和大于0的个数。
解题思路
首先介绍前缀和的概念,前缀和就是求和1~1 、1 ~ 2、1 ~3、…1 ~ n的和,把他们放入一个数组sum,sum[i]表示1 ~ i的和! 同理后缀和就是求n ~n、n ~n-1…n ~1的和!
这道题和前缀和的关系
首先我们求出前缀和,然后用sum[j]-sum[j] (i<j)
如果sum[j]-sum[i]>0证明 i -1~j 序列和大于0,则子串i -1~j的和大于0
通过上述讲解,我们找出所有满足sum[j]-sum[i]>0的序列就可以得出答案
如何找呢?
我们学习过逆序对问题,这个问题使用归并排序找出所有ai>aj,并且i<j的个数!
但是我们上述分析是sum[j]>sum[i],j>i这是顺序对
怎么办呢?
我们可以求出后缀和,只要后缀和满足sum[i]-sum[j]>0即sum[i]>sum[j]且i<j找出这样的逆序对,就可以得出正确答案!
不懂逆序对,看这个超链接!
逆序对问题–归并排序求解
完整代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,ans;
int t[maxn],arr[maxn],sum[maxn];
//归并排序
void merge_sort(int l,int r)
{
if(l==r)return;
int mid=(l+r)/2;
merge_sort(l,mid);
merge_sort(mid+1,r);
for(int i=l; i<=r; i++)
{
t[i]=sum[i];
}
int i=l,j=mid+1;
for(int cur=l; cur<=r; cur++)
{
if(i>mid) sum[cur]=t[j++];
else if(j>r) sum[cur]=t[i++];
else if(t[j]>=t[i]) sum[cur]=t[i++];
else
{
ans+=mid-i+1;//统计答案
sum[cur]=t[j++];
}
}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&arr[i]);
}
//求后缀和
for(int i=n; i>=1; i--)
{
sum[i]=sum[i+1]+arr[i];
}
merge_sort(1,n+1);
printf("%lld",ans);
return 0;
}
小细节
大家有没有注意到,merge_sort(1,n+1)传入的是n+1而不是n!
明明有n对逆序对啊,为什么呢?
因为,如果序列i ~末尾这个序列也满足和大于0,必须要用这种后缀和表示
sum[i]-sum[末尾+1],注意是末尾+1,这样才能求得 i ~末尾 的序列和!所以要传入第n+1为0的后缀和!
讲解完毕!
如果对你有帮助多多点赞支持!