前言:要不是bzoj刚好在我做完这题时又挂了,我才懒得写呢
这题感觉不难吧。。
首先我们知道任何一个子串两个后缀的差值,于是我们先把全部后缀弄出来
然后排个序
很容易吧问题转换为<=r的减去<l的有多少个
我们暴力枚举左端点,接着对于当前的这个后缀,我们看看比他后的有多少的后缀满足条件
至于怎么判断,我们可以算出后面的后缀值的大小,接着在一开始弄出来里面的二分就好了
我们每处理完一位就往右挪一位,怎么删除呢,我们开个树状数组就好了嘛,来维护这个后缀之前有多少个数,具体看代码
总的之间复杂度是nlogn的
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=100005;
LL n;
LL a[N];
LL sum=0;
struct qq
{LL x,id;
}lalal[N];
LL ans=0;
LL f[N];
LL lb (LL x)
{return x&(-x);
}
void change (LL x,LL y)
{while (x<=n){f[x]+=y;x+=lb(x);}
}
LL get (LL x)
{LL tot=0;while (x>=1){tot=tot+f[x];x-=lb(x);}return tot;
}
LL find (LL x)//多少个数比这个大
{LL l=1,r=n;LL ok=-1;while (l<=r){LL mid=(l+r)>>1;if (lalal[mid].x>=x){ok=mid;r=mid-1;}else l=mid+1;}if (ok==-1) return 0;return get(n)-get(ok-1);
}
LL pos[N];
bool cmp (qq a,qq b){return a.x<b.x;}
int main()
{LL l,r;scanf("%lld%lld%lld",&n,&l,&r);if (l>r) {printf("0\n");return 0;}for (LL u=1;u<=n;u++) {scanf("%lld",&a[u]);sum=sum+a[u];}
// printf("%lld\n",sum);//n++;a[n]=0;memset(f,0,sizeof(f));for (LL u=n;u>=1;u--) lalal[u].x=lalal[u+1].x+a[u],lalal[u].id=u;sort(lalal+1,lalal+1+n,cmp);for (LL u=1;u<=n;u++) pos[lalal[u].id]=u;for (LL u=1;u<=n;u++) change(u,1);for (LL u=1;u<=n;u++){ change(pos[u],-1);ans=ans+find(sum-r)-find(sum-l+1);if (sum>=l&&sum<=r) ans++;//printf("%lld %lld %lld %lld %lld %lld\n",u,ans,sum-r,find(sum-r),sum-l+1,find(sum-l+1));//printf("%lld %lld %lld %lld %lld\n",sum,sum-r,find(sum-r),sum-l+1,find(sum-l+1));sum=sum-a[u];}printf("%lld\n",ans);return 0;
}