CF1042D - Petya and Array

Posted Luowaterbi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;

以上是关于CF1042D - Petya and Array的主要内容,如果未能解决你的问题,请参考以下文章

CF1042D - Petya and Array

Petya and Array CodeForces - 1042D (树状数组)

CF886C Petya and Catacombs

CF1082G Petya and Graph

CF832B Petya and Exam

1042.D Petya and Array 前缀 + 树状数组