[BJOI2016]回转寿司
题目描述
酷爱日料的小Z经常光顾学校东门外的回转寿司店。在这里,一盘盘寿司通过传送带依次呈现在小Z眼前。
不同的寿司带给小Z的味觉感受是不一样的,我们定义小Z对每盘寿司都有一个满意度。
例如小Z酷爱三文鱼,他对一盘三文鱼寿司的满意度为\(10\);小Z觉得金枪鱼没有什么味道,他对一盘金枪鱼寿司的满意度只有\(5\);小Z最近看了电影《美人鱼》,被里面的八爪鱼恶心到了,所以他对一盘八爪鱼刺身的满意度是\(-100\)。
特别地,小Z是个著名的吃货,他吃回转寿司有一个习惯,我们称之为“狂吃不止”。具体地讲,当他吃掉传送带上的一盘寿司后,他会毫不犹豫地吃掉它后面的寿司,直到他不想再吃寿司了为止。
今天,小Z再次来到了这家回转寿司店,\(N\)盘寿司将依次经过他的面前。其中,小Z对第\(i\)盘寿司的满意度为\(a_i\)。
小Z可以选择从哪盘寿司开始吃,也可以选择吃到哪盘寿司为止。他想知道共有多少种不同的选择,使得他的满意度之和不低于\(L\),且不高于\(R\)。
注意,虽然这是回转寿司,但是我们不认为这是一个环上的问题,而是一条线上的问题。即,小Z能吃到的是输入序列的一个连续子序列;最后一盘转走之后,第一盘并不会再出现一次。
输入格式
第一行三个整数\(N\),\(L\),\(R\),表示寿司盘数,满意度的下限和上限。
第二行包含\(N\)个整数\(a_i\),表示小Z对寿司的满意度。
输出格式
一行一个整数,表示有多少种方案可以使得小Z的满意度之和不低于\(L\)且不高于\(R\)。
输入输出样例
输入
5 5 9
1 2 3 4 5
输出
6
说明/提示
对于\(100\%\)的数据,\(1\le N\le 10^5\),\(|a_i|\le 10^5\),\(1\le L,R\le 10^9\)。
树状数组
分析
由于是选择从哪盘寿司开始吃,吃到哪盘寿司为止,我们很显然能想到前缀和。
设\(sum(i)\)表示从第\(1\)盘寿司吃到第\(i\)盘寿司的满意度之和,我们只需要考虑满足如下条件的选择方案(从第\(j\)盘到第\(i\)盘)有多少个:
\(L\le sum(i)-sum(j-1)\le R\)
区间枚举,我们通常先枚举右端点,这是因为我们习惯从左往右枚举,对于当前枚举到的右端点,它左边的所有点之前一定都被枚举过,再处理左端点就会方便许多。当然也有先枚举区间长度再枚举左端点的方法,其实两者是同一种思想,都是为了使之后的枚举更加方便。
所以,我们可以把枚举到的\(sum(i)\)当作是固定的,再考虑\(sum(j-1)\),把原式化为:
\(sum(i)-R\le sum(j-1)\le sum(i)-L\)
这样,问题就转换为计算对于每一个\(sum(i)\),在\([sum(i)-R,sum(i)-L]\)范围内的\(sum(j-1)\)有多少个。
树状数组
树状数组其实和前缀和数组有些相像,修改一个位置上的信息都会影响到其后面位置上的信息,询问一个区间上的信息的方式也存在相似之处。只不过树状数组有二进制优化,单点修改和区间查询都是\(log\)级别的,而前缀和区间查询是\(O(1)\)的,但单点修改是\(O(n)\)的。因此,树状数组是一种支持快速区间操作的数据结构。
使用树状数组对一个问题进行维护,很多时候不直接将原数组下标作为树状数组下标。本题给定了\(N\)次查询\([sum(i)-R,sum(i)-L]\),我们就将\(sum(i)-R\)和\(sum(i)-L\)这样的范围界限作为树状数组下标。因为每次查询的是该范围内\(sum(j-1)\)的个数,我们也需要将\(sum(j-1)\)作为树状数组下标(\(1\le j\le N\))。
需要注意本题的数据范围,\(sum(i)-R\)和\(sum(i)-L\)都有可能是负数,并且也都有可能超过\(int\)型范围。因此,我们必须事先将需要作为树状数组下标的数值离散化。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=(int)1e5+5;
int tre[3*MAXN];
long long sum[MAXN],num[3*MAXN];
int m;
inline int lbt(int x){
return -x&x;
}
inline void upd(int x){
while(x<=m){
tre[x]++;
x+=lbt(x);
}
}
inline int qry(int x){
int ans=0;
while(x){
ans+=tre[x];
x-=lbt(x);
}
return ans;
}
int main(){
int n,l,r;
long long ans=0;
scanf("%d%d%d",&n,&l,&r);
for(register int i=1;i<=n;i++){
int a;
scanf("%d",&a);
sum[i]=sum[i-1]+a;
num[(i-1)*3+1]=sum[i-1];
num[(i-1)*3+2]=sum[i]-l;
num[(i-1)*3+3]=sum[i]-r-1;
}
sort(num+1,num+3*n+1);
m=unique(num+1,num+3*n+1)-num-1;
for(register int i=1;i<=n;i++){
upd(lower_bound(num+1,num+m,sum[i-1])-num);
ans+=qry(lower_bound(num+1,num+m,sum[i]-l)-num)-qry(lower_bound(num+1,num+m,sum[i]-r-1)-num);
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号