Loading

[解题记录] CF1042D Petya and Array

CF1042D Petya and Array

题意简述

有一个长度为 \(n\) 的序列 \(a\),和一个数 \(t\),求有多少个区间 \([l,r]\) 满足 \(a_l+a_{l+1}+...+a_{r} <t\)\(l\le r\)

\(1≤n≤200000\),\(∣t∣≤2⋅10^{14}\)


解题思路

一道前缀和+权值线段树的好题。

题目要求我们找一个区间 \([l,r]\) ,由于范围的限制,肯定不能直接枚举,那么对于枚举区间上的和,我们就应想到去使用前缀和,则满足

的条件便转化为:

\[\sum_{i=l}^r a_i<t \]

\[\sum_{i=1}^r a_i- \sum_{i=1}^{l-1} a_i<t \]

\[\sum_{i=1}^r a_i<t+ \sum_{i=1}^{l-1} a_i \]

那么就可以去枚举 \(l\in[1,n]\) ,找到满足条件的 \(r\) 即可(注意要倒序枚举)

这个式子其实也可以理解为固定左端点找满足条件的右端点

利用权值线段树维护即可,离散化太麻烦,可以使用动态开点的权值线段树

要注意的一点是,因为没使用离散化,数组的下标又不能存负数,所以需要给维护的值赋上一个很大的初值


\(Code\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
const int N=4e5+10,MAXN=1e15;
int sum[N<<2],n,m,a[N],ls[N<<2],t,rs[N<<2],ans,tot,rt;
void pushup(int p){
    sum[p]=sum[ls[p]]+sum[rs[p]];
}
void add(int &k){
    k=++tot;
}
void upd(int &k,int l,int r,int x){
    if(!k) add(k);
    if(l==r){
	sum[k]++;
	return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) upd(ls[k],l,mid,x);
    else upd(rs[k],mid+1,r,x);
    pushup(k);
}
int query(int p,int l,int r,int x,int y){
    if(x<=l&&y>=r){
	return sum[p];
    }
    int res=0,mid=(l+r)>>1;
    if(x<=mid) res+=query(ls[p],l,mid,x,y);
    if(y>mid) res+=query(rs[p],mid+1,r,x,y);
    return res;
}
signed main(){
    n=read();t=read();
    for(int i=1;i<=n;++i) a[i]=read(),a[i]+=a[i-1];
    for(int i=n;i;--i){
	upd(rt,1,MAXN<<1,a[i]+MAXN);
	ans+=query(rt,1,MAXN<<1,1,a[i-1]+MAXN+t-1);//-1是y因为<
    }
    printf("%lld\n",ans);
    return 0;
}

posted @ 2022-01-25 22:04  Miraii  阅读(45)  评论(0)    收藏  举报