[解題記錄] CF1042D Petya and Array
阿新 • • 發佈:2022-01-26
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]\)
這個式子其實也可以理解為固定左端點找滿足條件的右端點
利用權值線段樹維護即可,離散化太麻煩,可以使用動態開點的權值線段樹
要注意的一點是,因為沒使用離散化,陣列的下標又不能存負數,所以需要給維護的值賦上一個很大的初值
\(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; }