bzoj-4627 [BeiJing2016]回转寿司 hash+权值线段树

4627: [BeiJing2016]回转寿司

 题意:给你n个数,求有多少个子序列的和在[l,r]之间。

先求一个前缀和,那么区间和直接用两个前缀相减即可,那么要满足条件:l<=sum[i]-sum[j]<=r,即sum[i]-r<=sum[j]<=sum[i]-l。基于数据范围较大,需要将所有的值hash一下,然后扔进权值线段树中,统计区间值出现的次数。

const int N=3e5+10;
inline char nc()
{
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
//inline int sc()
//{
//    char ch=nc();
//    int sum=0;
//    while(!(ch>='0'&&ch<='9'))ch=nc();
//    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
//    return sum;
//}
inline int sc()
{
    char c;
    int ret,sgn;
    if(c=nc(),c==EOF) return 0;
    while(c!='-'&&(c<'0'||c>'9')) c=nc();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=nc(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    return ret*sgn;
}
ll a[N],ha[N],l,r;
int n,nn;
struct node
{
    int l,r,x;
} e[N<<2];
void build(int l,int r,int k)
{
    e[k].l=l,e[k].r=r,e[k].x=0;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
}
void update(int id,int k)
{
    if(e[k].l==id&&e[k].r==id)
    {
        e[k].x++;
        return ;
    }
    int mid=(e[k].l+e[k].r)/2;
    if(id<=mid) update(id,2*k);
    else update(id,2*k+1);
    e[k].x=e[k*2].x+e[k*2+1].x;
}
int query(int l,int r,int k)
{
    if(l==e[k].l&&r==e[k].r) return e[k].x;
    int mid=(e[k].l+e[k].r)/2;
    if(r<=mid) return query(l,r,2*k);
    else if(l>mid ) return query(l,r,2*k+1);
    else return query(l,mid,2*k)+query(mid+1,r,2*k+1);
}
void solve()
{
    nn=0;
    for(int i=0; i<=n; i++)
    {
        ha[++nn]=a[i];
        ha[++nn]=a[i]+l;
        ha[++nn]=a[i]+r;
    }
    sort(ha+1,ha+nn+1);
    nn=unique(ha+1,ha+nn+1)-ha-1;
    build(1,nn+1,1);
    ll ans=0;
    for(int i=n; i>=0; i--)
    {
        int pos1=lower_bound(ha+1,ha+nn+1,a[i]+l)-ha;
        int pos2=lower_bound(ha+1,ha+nn+1,a[i]+r)-ha;
        int num=query(pos1,pos2,1);
        ans+=num;
        update(lower_bound(ha+1,ha+nn+1,a[i])-ha,1);
    }
    printf("%lld\n",ans);
}
int main()
{
    while(~scanf("%d%lld%lld",&n,&l,&r))
    {
        a[0]=0;
        for(int i=1; i<=n; i++) a[i]=sc(),a[i]+=a[i-1];
        solve();
    }
    return 0;
}

//5 5 9
//1 2 3 4 5
//5 0 80
//1 5 9 4 1
//5 0 80
//1 5 9 4 0


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值