[解题记录] 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;
}

浙公网安备 33010602011771号