题目:
求满足区间和小于t的区间数量。
题解:
区间的数量可以转换成固定右端点,求满足条件的左端点的个数。
满足条件的区间 [ l + 1 , r ] [l+1,r] [l+1,r]:
s u m [ r ] − s u m [ l ] < t s u m [ r ] − t < s u m [ l ] sum[r]-sum[l]sum[r]−sum[l]<tsum[r]−t<sum[l]
会发现这是一个关于 s u m sum sum的单点修改(增加),区间(前缀)查询的问题。可以选择线段树、树状数组等方法解决。我选择用权值线段树。
首先,权值线段树求 x x x的rank本质上就是求前缀和。那我们可以转换一下问题,对于当前位置 i i i,有 i i i个前缀。其中有 ∑ j = 0 i − 1 ( s u m [ i ] − t ≥ s u m [ j ] ) \sum_{j=0}^{i-1}(sum[i]-t\geq sum[j]) ∑j=0i−1(sum[i]−t≥sum[j])个是不合法的。相减即可得到答案。
-
前缀和可能很大,但是数量很少,需要离散化(我没有用规范的离散化,我只适用快排之后用下标表示数字);
-
注意当区间长度为 i i i的时候,对应 s u m [ l = 0 ] = 0 sum[l=0]=0 sum[l=0]=0,这个要在开始循环之前加进去。用 s u m [ i ] sum[i] sum[i]表示前缀和时, s u m [ 0 ] sum[0] sum[0]正好是这个作用,所以离散化时 s u m sum sum数组长度不能是 n n n,而是 n + 1 n+1 n+1。但是由于离散化,update的时候对应的不再是 s u m [ 0 ] sum[0] sum[0]的下标 0 0 0,而是离散化后的位置!因为前缀和可能存在小于0的情况。
-
要求的是小于等于 s u m [ i ] − t sum[i]-t sum[i]−t的数量,那么lower_bound显然不合适:
- 因为当离散化后的 s u m sum sum没有正好等于 s u m [ i ] − t sum[i]-t sum[i]−t的数的时候,此时返回的下标比 s u m [ i ] − t sum[i]-t sum[i]−t大,那计算的时候不合法情况会增多;
- 由于我用的不是完全的离散化,当离散化后的 s u m sum sum有多个等于 s u m [ i ] − t sum[i]-t sum[i]−t的数时,lower_bound返回的是第一个的下标(离散后的值),就会导致计算时缺少情况。
-
选用upper_bound,对计算后的下标 − 1 -1 −1即可。
AC代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include