连续非空子序列和大于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。
示例1

输入

复制
5
9 -6 -5 4 8

输出

复制
9


首先题目中提到这个连续子序列,那么我们可以用前缀和来进行o(1)求出来的
然后尝试写一个暴力进行优化

#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;
} 

 

posted @ 2021-06-20 11:44  lipu123  阅读(496)  评论(0)    收藏  举报