Description
求有多少个区间[l,r][l,r][l,r满足sum[l,r]<t
n<200000,|di|<1e9,t<2e14
Solution
权值线段树为l,r表示权值的线段树,相当于对桶进行线段树操作,
更形象一点,可称为值域线段树
设a数组为前缀和
则满足条件的[l,r]符合a[r]-a[l-1]<t,
即a[r]<t+a[l-1]
由于数据太大,先离散(怎么离散不妨自己想一想,注意考虑a[0])
然后从后往前扫一遍,加入a[i],再用线段树统计它贡献的答案
Code
#include <cstdio> #include <cstdlib> #include <algorithm> #define ll long long using namespace std; const int N=2e5+10; struct node { ll v; int id; bool operator <(const node &o)const { return v<o.v; } }a[N*2]; struct mode { int l,r,lc,rc; ll sum; }f[N*4]; int n,cnt,tot,rt; ll d[N*2],t,ans; void build(int l,int r,int &g) { g=++tot; f[g].l=l,f[g].r=r; if(l==r) return ; int mid=(l+r)>>1; build(l,mid,f[g].lc); build(mid+1,r,f[g].rc); } void push_up(int g) { f[g].sum=f[f[g].lc].sum+f[f[g].rc].sum; } void add(int g,int x) { if(f[g].l==f[g].r) f[g].sum++; else { int mid=(f[g].l+f[g].r)>>1; if(x<=mid) add(f[g].lc,x); else add(f[g].rc,x); push_up(g); } } ll get(int g,int l,int r) { if(l>r) return 0; if(f[g].l>=l && f[g].r<=r) return f[g].sum; int mid=(f[g].l+f[g].r)>>1; if(r<=mid) return get(f[g].lc,l,r); else if(l>mid) return get(f[g].rc,l,r); else return get(f[g].lc,l,mid)+get(f[g].rc,mid+1,r); } int main() { scanf("%d%lld",&n,&t); n++; for(int i=1;i<=n;i++) { if(i>1) scanf("%lld",&a[i].v); else a[i].v=0; a[i].v+=a[i-1].v,a[i+n].v=a[i].v+t; a[i].id=i,a[i+n].id=i+n; } sort(a+1,a+n*2+1); for(int i=1;i<=n*2;i++) { if(i==1 || a[i].v!=a[i-1].v) cnt++; d[a[i].id]=cnt; } build(1,cnt,rt); for(int i=n;i>1;i--) { add(rt,d[i]); ans+=get(rt,1,d[i-1+n]-1); } printf("%lld\n",ans); return 0; }
](ssum