题解 CF1042D 【Petya and Array】
CreeperLordVader · · 题解
居然没有权值线段树解法!!!
那我来一个
本题细节真的巨多,思路1分钟,代码5小时。。。
首先,为了能
然后考虑题设限制
求有多少个区间
由于前缀和的存在,
于是变成了a(r)<a(l-1)+t
于是可以扫描一遍a ,在每个前缀l 处我们只需求出有多少数r 满足上式
可以用权值线段树来维护,只需求出
由于求的前缀不包括l-1自身,所以还要在进入下一个前缀前删除下一个前缀
因为数很大,需要离散化,具体实现略微复杂
我最开始的时候右儿子打成左儿子,调了一小时,后来离散化以后没开long long又花了几小时...
#include<bits/stdc++.h>
#define ll long long
#define update(o) sz[o]=sz[o<<1]+sz[o<<1|1]
using namespace std;
const int MAXN=200005;
ll val[200005];
int cnt[200005];
int n,m;;
ll ans;
ll pref;
ll t,a[200005];
ll num[200005];
int id[200005];
int sz[MAXN<<2];
map<ll,int>times;
template<typename type>
void read(type& x)
{
char c=getchar();
int f=1;x=0;
while(c<'0'||c>'9')
{
if(c=='-')f*=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
x*=f;
}
void build(int o,int l,int r)
{
if(l==r)
{
sz[o]=cnt[l];
return ;
}
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
update(o);
}
int query(int o,int l,int r,int k)
{
if(!k)return 0;
if(r<=k)
{
return sz[o];
}
int mid=(l+r)>>1,sum=0;
sum+=query(o<<1,l,mid,k);
if(k>mid)sum+=query(o<<1|1,mid+1,r,k);
return sum;
}
void remove(int o,int l,int r,int k)
{
if(l==r)
{
sz[o]--;
return ;
}
int mid=(l+r)>>1;
if(k<=mid)remove(o<<1,l,mid,k);
else remove(o<<1|1,mid+1,r,k);
update(o);
}
int main()
{
read(n);
read(t);
for(int i=1;i<=n;i++)
{
read(num[i]);
a[i]=a[i-1]+num[i];
times[a[i]]++;
}
sort(a+1,a+n+1);
m=unique(a+1,a+n+1)-a-1;
for(int i=0;i<m;i++)
{
val[i+1]=a[i+1];
cnt[i+1]=times[a[i+1]];
}
build(1,1,m);
for(int i=0;i<n;i++)
{
pref+=num[i];
int k=lower_bound(val+1,val+m+1,pref+t-1)-val;
if(k>m)k=m;
if(val[k]>=pref+t)k--;
if(k)ans+=(ll)query(1,1,m,k);
int p=lower_bound(val+1,val+m+1,pref+num[i+1])-val;
remove(1,1,m,p);
}
printf("%lld\n",ans);
}