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