https://www.lydsy.com/JudgeOnline/problem.php?id=4627
这是一道值域线段树的模板题。
首先我们明确一下值域线段树的定义:
值域线段树用来统计插进去的元素在区间[L,R]中的个数
那么针对这一道题,这一题询问的是:
给定N个数,询问这N个数里面有多少个子区间的和大于等于L且小于等于R
我们算计算出这N个数的前缀和,用sum[i]来表示,用[i,j]来表示一个区间,那么题目就可以转化为:
求满足L≤sum[j]−sum[i]≤RL≤sum[j]−sum[i]≤R的有多少个
我们把这个式子进行变形,得到:
sum[j]−R≤sum[i]≤sum[j]−Lsum[j]−R≤sum[i]≤sum[j]−L
也就是求满足上面式子的区间有多少个,那么我们先把0插进去,然后遍历一下这N个数,询问每一个区间[sum[i]-R,sum[i]-L]求和,再把sum[i]依次插入
https://www.cnblogs.com/lwq12138/p/5674554.html
https://blog.csdn.net/blessLZH0108/article/details/77072022
s[]表示前缀和,题目要求的是有多少对(i,j)满足L≤s[j]-s[i]≤R(i<j),变形一下得到s[j]-R≤s[i]≤s[j]-L
因此我们只需要遍历一遍s[]数组,每次对于当前的前缀和s,我们先在线段树上查询区间[s-R,s-L]内有多少个数,然后再把s加入到线段树中,最终求和即可
https://www.cnblogs.com/GXZlegend/p/7662744.html
酷爱日料的小Z经常光顾学校东门外的回转寿司店。在这里,一盘盘寿司通过传送带依次呈现在小Z眼前。不同的寿
司带给小Z的味觉感受是不一样的,我们定义小Z对每盘寿司都有一个满意度,例如小Z酷爱三文鱼,他对一盘三文
鱼寿司的满意度为10;小Z觉得金枪鱼没有什么味道,他对一盘金枪鱼寿司的满意度只有5;小Z最近看了电影“美
人鱼”,被里面的八爪鱼恶心到了,所以他对一盘八爪鱼刺身的满意度是-100。特别地,小Z是个著名的吃货,他
吃回转寿司有一个习惯,我们称之为“狂吃不止”。具体地讲,当他吃掉传送带上的一盘寿司后,他会毫不犹豫地
吃掉它后面的寿司,直到他不想再吃寿司了为止。今天,小Z再次来到了这家回转寿司店,N盘寿司将依次经过他的
面前,其中,小Z对第i盘寿司的满意度为Ai。小Z可以选择从哪盘寿司开始吃,也可以选择吃到哪盘寿司为止,他
想知道共有多少种不同的选择,使得他的满意度之和不低于L,且不高于R。注意,虽然这是回转寿司,但是我们不
认为这是一个环上的问题,而是一条线上的问题。即,小Z能吃到的是输入序列的一个连续子序列;最后一盘转走
之后,第一盘并不会再出现一次。
Input
第一行包含三个整数N,L和R,分别表示寿司盘数,满意度的下限和上限。
第二行包含N个整数Ai,表示小Z对寿司的满意度。
N≤100000,|Ai|≤100000,0≤L, R≤10^9
Output
仅一行,包含一个整数,表示共有多少种选择可以使得小Z的满意度之和
不低于L且不高于R。
Sample Input
5 5 9
1 2 3 4 5
1 2 3 4 5
Sample Output
6
https://blog.csdn.net/riba2534/article/details/77109677这是一道值域线段树的模板题。
首先我们明确一下值域线段树的定义:
值域线段树用来统计插进去的元素在区间[L,R]中的个数
那么针对这一道题,这一题询问的是:
给定N个数,询问这N个数里面有多少个子区间的和大于等于L且小于等于R
我们算计算出这N个数的前缀和,用sum[i]来表示,用[i,j]来表示一个区间,那么题目就可以转化为:
求满足L≤sum[j]−sum[i]≤RL≤sum[j]−sum[i]≤R的有多少个
我们把这个式子进行变形,得到:
sum[j]−R≤sum[i]≤sum[j]−Lsum[j]−R≤sum[i]≤sum[j]−L
也就是求满足上面式子的区间有多少个,那么我们先把0插进去,然后遍历一下这N个数,询问每一个区间[sum[i]-R,sum[i]-L]求和,再把sum[i]依次插入
https://www.cnblogs.com/lwq12138/p/5674554.html
ll sum[N],Max,Min,ans;
inline
void
read(
int
&v){
char
ch,fu=0;
for
(ch=
'*'
; (ch<
'0'
||ch>
'9'
)&&ch!=
'-'
; ch=
getchar
());
if
(ch==
'-'
) fu=1, ch=
getchar
();
for
(v=0; ch>=
'0'
&&ch<=
'9'
; ch=
getchar
()) v=v*10+ch-
'0'
;
if
(fu) v=-v;
}
inline
void
update(ll l,ll r,ll x,
int
&p)
{
if
(!p) p=++tot;
if
(l==r)
{
t[p]++;
return
;
}
ll mid=(l+r)>>1;
if
(x<=mid) update(l,mid,x,ls[p]);
else
update(mid+1,r,x,rs[p]);
t[p]=t[ls[p]]+t[rs[p]];
}
inline
void
solve(ll l,ll r,ll x,ll y,
int
p)
{
if
(x<=l&&r<=y)
{
ans+=t[p];
return
;
}
ll mid=(l+r)>>1;
if
(x<=mid&&ls[p]) solve(l,mid,x,y,ls[p]);
if
(y>mid&&rs[p]) solve(mid+1,r,x,y,rs[p]);
}
read(n),read(l),read(r);
for
(i=1;i<=n;i++)
{
read(x);
sum[i]=sum[i-1]+x;
Max=max(Max,sum[i]);
Min=min(Min,sum[i]);
}
update(Min,Max,0,rt);
for
(i=1;i<=n;i++)
{
solve(Min,Max,sum[i]-r,sum[i]-l,rt);
update(Min,Max,sum[i],rt);
}
s[]表示前缀和,题目要求的是有多少对(i,j)满足L≤s[j]-s[i]≤R(i<j),变形一下得到s[j]-R≤s[i]≤s[j]-L
因此我们只需要遍历一遍s[]数组,每次对于当前的前缀和s,我们先在线段树上查询区间[s-R,s-L]内有多少个数,然后再把s加入到线段树中,最终求和即可
设s[i]表示前缀和。开一棵权值线段树,把整个序列扫一遍,每到一个数就把以这个数结尾对答案的贡献计算一下,然后把s[i]扔进线段树里面就好了。
离散化+树状数组
把序列和转化为前缀相减,即选出满足的的数对个数。
那么我们枚举,即可得到的范围,要求的是以前的满足条件的的个数。可以维护1到当前位置树状数组,在树状数组中查询个数,最后再把该数加入到树状数组中。由于数据范围大,因此需要离散化。
时间复杂度
ll sum[N] , v[N];
int
f[N] , n;
inline
void
add(
int
x)
{
int
i;
for
(i = x ; i <= n + 1 ; i += i & -i) f[i] ++ ;
}
inline
int
query(
int
x)
{
int
i , ans = 0;
for
(i = x ; i ; i -= i & -i) ans += f[i];
return
ans;
}
int
main()
{
int
i;
ll l , r , ans = 0;
scanf
(
"%d%lld%lld"
, &n , &l , &r);
for
(i = 1 ; i <= n ; i ++ )
scanf
(
"%lld"
, &sum[i]) , sum[i] += sum[i - 1] , v[i] = sum[i];
sort(now);
for
(i = 0 ; i <= n ; i ++ ) ans += query(upper_bound(now , sum[i] - l) - v - 1) - query(lower_bound(now , sum[i] - r) - v - 1) , add(lower_bound(now , sum[i]) - v);