CF1042D - Petya and Array

题目:

求满足区间和小于t的区间数量。

题解:

区间的数量可以转换成固定右端点,求满足条件的左端点的个数。

满足条件的区间 [ l + 1 , r ] [l+1,r] [l+1,r]
s u m [ r ] − s u m [ l ] < t s u m [ r ] − t < s u m [ l ] sum[r]-sum[l]<t\\ sum[r]-t<sum[l] sum[r]sum[l]<tsum[r]t<sum[l]
会发现这是一个关于 s u m sum sum单点修改(增加),区间(前缀)查询的问题。可以选择线段树、树状数组等方法解决。我选择用权值线段树

首先,权值线段树求 x x x的rank本质上就是求前缀和。那我们可以转换一下问题,对于当前位置 i i i,有 i i i个前缀。其中有 ∑ j = 0 i − 1 ( s u m [ i ] − t ≥ s u m [ j ] ) \sum_{j=0}^{i-1}(sum[i]-t\geq sum[j]) j=0i1(sum[i]tsum[j])个是不合法的。相减即可得到答案。

  • 前缀和可能很大,但是数量很少,需要离散化(我没有用规范的离散化,我只适用快排之后用下标表示数字);

  • 注意当区间长度为 i i i的时候,对应 s u m [ l = 0 ] = 0 sum[l=0]=0 sum[l=0]=0,这个要在开始循环之前加进去。用 s u m [ i ] sum[i] sum[i]表示前缀和时, s u m [ 0 ] sum[0] sum[0]正好是这个作用,所以离散化时 s u m sum sum数组长度不能是 n n n,而是 n + 1 n+1 n+1。但是由于离散化,update的时候对应的不再是 s u m [ 0 ] sum[0] sum[0]的下标 0 0 0,而是离散化后的位置!因为前缀和可能存在小于0的情况。

  • 要求的是小于等于 s u m [ i ] − t sum[i]-t sum[i]t的数量,那么lower_bound显然不合适:

    • 因为当离散化后的 s u m sum sum没有正好等于 s u m [ i ] − t sum[i]-t sum[i]t的数的时候,此时返回的下标比 s u m [ i ] − t sum[i]-t sum[i]t大,那计算的时候不合法情况会增多;
    • 由于我用的不是完全的离散化,当离散化后的 s u m sum sum有多个等于 s u m [ i ] − t sum[i]-t sum[i]t的数时,lower_bound返回的是第一个的下标(离散后的值),就会导致计算时缺少情况。
  • 选用upper_bound,对计算后的下标 − 1 -1 1即可。

AC代码:

#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <algorithm>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#include <cstdlib>
#include <stack>
#include <cstring>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define lep(i,a,b) for(int i=(a);i>=(b);i--) 
#define lepp(i,a,b) for(int i=(a);i>(b);i--)
#define pii pair<int,int>
#define pll pair<long long,long long>
#define mp make_pair
#define All(x) x.begin(),x.end() 
#define ms(a,b) memset(a,b,sizeof(a)) 
#define INF 0x3f3f3f3f
#define INFF 0x3f3f3f3f3f3f3f3f 
#define multi int T;scanf("%d",&T);while(T--) 
using namespace std;
typedef long long ll;
typedef double db;
const int N=2e5+5;
const int mod=1e9+7;
const db eps=1e-6;                                                                            
const db pi=acos(-1.0);
int n,m,tr[N<<2],a[N];
ll sum[N],t;
void update(int d,int l,int r,int x){
    if(l==r){
        tr[d]+=1;
        return ;
    }
    int lc=d<<1,rc=lc|1,mid=(l+r)>>1;
    if(x>mid) update(rc,mid+1,r,x);
    else update(lc,l,mid,x);
    tr[d]=tr[lc]+tr[rc];
}
ll query(int d,int l,int r,int x){
    int lc=d<<1,rc=lc|1,mid=(l+r)>>1;
    if(x<l) return 0;
    if(tr[d]==0||x>=r) return tr[d];
    if(x>mid) return tr[lc]+query(rc,mid+1,r,x);
    else return query(lc,l,mid,x);
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("D:\\work\\data.in","r",stdin);
    #endif
    cin>>n>>t;
    rep(i,1,n){
        cin>>a[i];
        sum[i]=a[i]+sum[i-1];
    }
    // rep(i,1,n) sum[i+n]=sum[i]-t;
    sort(sum,sum+n+1);//实际上是一个离散化的过程,前缀和值域太大,数量很少
    ll res=0,ans=0;
    // if(a[1]<t) ans++;
    // update(1,1,n,lower_bound(sum+1,sum));
    // update(1,0,n,0); 错误写法,这个0是为了保证只有自己一项的前缀和;但是0不一定在“0位置”
    update(1,0,n,lower_bound(sum,sum+n+1,0)-sum);
    rep(i,1,n){
        res+=a[i];
        int pos_t=upper_bound(sum,sum+n+1,res-t)-sum;
        ans+=i;
        ans-=query(1,0,n,pos_t-1);
        int pos=lower_bound(sum,sum+n+1,res)-sum;
        update(1,0,n,pos);
    }
    cout<<ans<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值