bzoj4627

前言:要不是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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值