Codeforces Round #510 (Div. 2) D. Petya and Array (权值线段树)
-
题意:有一组数,问你有多少子区间的和\(<t\).
-
题解:先用前缀和优化,可以表示成\(sum[r]-sum[l-1]<t\).移项得到:\(sum[l-1]>sum[r]-t\).那么就可以用一棵权值线段树来解决此问题,每次插入\(sum[r]\)的时候查询有多少满足条件的\(sum[l-1]\),这里不懂的可以去看看逆序对的求法。这题还有个问题,权值太大了,需要离散化,但是我懒(,用动态开点也行。
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 5e5 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} #define int long long int n; ll t; ll a[N]; ll sum[N]; ll tr[N<<4]; ll ls[N<<4],rs[N<<4]; ll base=1e15; ll rt,idx; void update(int &u,int l,int r,ll x){ if(!u) u=++idx,ls[u]=rs[u]=0; if(l==r){ tr[u]++; return; } ll mid=(l+r)>>1; if(x<=mid) update(ls[u],l,mid,x); else update(rs[u],mid+1,r,x); tr[u]=tr[ls[u]]+tr[rs[u]]; } ll query(int u,int l,int r,int x,int y){ if(l>=x && r<=y) return tr[u]; ll res=0; ll mid=(l+r)>>1; if(x<=mid) res+=query(ls[u],l,mid,x,y); if(y>mid) res+=query(rs[u],mid+1,r,x,y); return res; } signed main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>t; for(int i=1;i<=n;++i){ cin>>a[i]; sum[i]=sum[i-1]+a[i]; } update(rt,1,base<<1,0+base); ll ans=0; for(int i=1;i<=n;++i){ ans+=query(rt,1,base<<1,sum[i]-t+base+1,base<<1); update(rt,1,base<<1,sum[i]+base); } cout<<ans<<'\n'; return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮