连续非空子序列和大于0(离散化+树状数组+前缀和)
链接:https://ac.nowcoder.com/acm/contest/11212/F
来源:牛客网
有一天,你路过机房,发现有两个人在讨论
溪染:喂,叁秋,你知道什么是连续非空子序列嘛?
叁秋:知道啊!
溪染:举个栗子?
叁秋:如果这里有个数组为{1,2,3},那么它的连续非空子序列有{1},{2},{3},{1,2},{2,3},{1,2,3}
(这里定义的连续非空子序列是指数组中连续的一段,所以不包括{1,3}这种不连续的)
溪染:那我这里有一个数组,数组里的每一个数都在int范围内,共n个数,编号a1a2...an,你能求出它有多少个连续非空子序列满足序列内数字和大于0吗?
叁秋:这不有手就行嘛,我直接暴力枚举连续非空子序列的左右范围,再暴力统计一遍不就可以了嘛!
溪染:如果我告诉你这个数组里面有10^6个数呢?
叁秋:啊,这?
叁秋:喂!那个偷听的!我早就发现你了!帮我解决这个问题,我就不计较了你偷听我们的谈话了。
简化题意:给定一个数 n, 再给出长度为 n的数列 ai,求有多少连续非空子序列使得序列中的数之和大于0。
链接:https://ac.nowcoder.com/acm/contest/11212/F
来源:牛客网
来源:牛客网
输入描述:
第一行输入一个正整数n(1≤n≤1e6),表示数组的大小。
第二行输入,n{n}n个数ai(−2e31≤ai≤2e31−1),表示数组中的n个数。
输出描述:
仅一行,表示问题的答案,即输入的数组有多少个连续非空子序列满足序列内数字和大于0。
#include<iostream> #include<algorithm> using namespace std; const int maxn=3e6+10; typedef long long ll; ll sum[maxn]; int n,a[maxn]; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; } int ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++){ if(sum[i]>sum[j-1]){ ans++; } } } cout<<ans<<endl; }
上面这个代码显然是不行的,我们要考虑的是把第二个for循环来优化掉,
仔细看一下,第二份for循环不就是求小于sum[i]的数的个数吗,这个可以用树状数组或者线段树来求出来的啊,但是我们需要进行离散化
#include<iostream> #include<algorithm> using namespace std; const int maxn=3e6+10; typedef long long ll; ll c[maxn]; ll a[maxn]; ll sum[maxn]; ll id[maxn]; int n,cnt=0; int lowbit(int x){ return x&-x; } void add(int x,ll k){ for(int i=x;i<=n;i+=lowbit(i)){ c[i]+=k; } } ll get(int x){ ll ans=0; for(int i=x;i;i-=lowbit(i)){ ans+=c[i]; } return ans; } int get_id(ll x){//离散化 return lower_bound(id+1,id+cnt+1,x)-id; } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; id[++cnt]=sum[i]; } sort(id+1,id+cnt+1); cnt=unique(id+1,id+cnt+1)-(id+1); ll ans=0; for(int i=1;i<=n;i++){ // for(int j=1;j<=i;j++){ // if(sum[i]>sum[j-1]){ // ans++; // } // } int p=get_id(sum[i]); if(sum[i]>0){ ans++; } add(p,1); ans+=get(p-1); } cout<<ans<<endl; return 0; }