P9681 幽默的世界。 题解
HFanGDoDM
·
·
题解
前置知识
二分法
题意简述
给定一个长度为 n 的序列 a。q 次询问,每次询问给定 l,r,求满足以下条件的数对 (l',r') 个数。
数据范围
子任务 0
#### 子任务 1
$1\leqslant n,q\leqslant 50$,$1\leqslant l\leqslant r\leqslant n$,$-10^9\leqslant a_i\leqslant 10^9$。
#### 子任务 2
$1\leqslant n,q\leqslant 3000$,$1\leqslant l\leqslant r\leqslant n$,$-10^9\leqslant a_i\leqslant 10^9$。
#### 子任务 3
$1\leqslant n,q\leqslant 2\times 10^5$,$1\leqslant l\leqslant r=n$,$-10^9\leqslant a_i\leqslant 10^9$。
#### 子任务 4
$1\leqslant n,q\leqslant 2\times 10^5$,$1=l\leqslant r\leqslant n$,$-10^9\leqslant a_i\leqslant 10^9$。
#### 子任务 5
$1\leqslant n,q\leqslant 2\times 10^5$,$1\leqslant l\leqslant r\leqslant n$,$-10^9\leqslant a_i\leqslant 10^9$。
# 解题思路
## 子任务 0
### 做法
若 $n=4,q=3$,则答案分别为 $1,2,3$;若 $n=7,q=6$,则答案分别为 $1,2,4,0,2,1$。
### 正确性证明
$n=4,q=3$ 的答案在 **样例输出 #1** 中给出,分别为 $1,2,3$;$n=7,q=6$ 的答案在 **样例输出 #2** 中给出,分别为 $1,2,4,0,2,1$。
### 具体实现
判断 $n,q$ 的值。若 $n=4,q=3$,则输出三行三个整数,分别为 $1,2,3$;若 $n=7,q=6$,则输出六行六个整数,分别为 $1,2,4,0,2,1$。
### 时间复杂度分析
输出一次,复杂度为 $O(1)$,可以通过 **子任务 0**。
### 参考核心代码
```cpp
if(n==4&&q==3)
printf("1\n2\n3");
if(n==7&&q==6)
printf("1\n2\n4\n0\n2\n1");
```
### 期望得分
$0$ 分。
## 子任务 1
### 做法
对于每次询问,枚举所有可能的 $(l',r')$。根据定义,检查 $\displaystyle\sum_{i=l'}^{r'}a_i$ 是否大于 $0$。若 $\displaystyle\sum_{i=l'}^{r'}a_i\leqslant0$,则该 $(l',r')$ 不符合题意。并枚举该区间内所有可能的 $x$ 和 $y$,判断是否有 $l'\leqslant x\leqslant y\lt r',\displaystyle\sum_{i=x}^ya_i\leqslant0$。若存在一个 $(x,y)$,满足 $\displaystyle\sum_{i=x}^ya_i\gt0$,则该 $(l',r')$ 不符合题意。
最后可以求出所有符合题意的 $(l',r')$ 个数。
### 正确性证明
由于我们每次枚举了 **所有可能的 $(l',r')$**,并**按照定义**,逐一检查了 $(l',r')$ 是否符合题意,因此该思路是正确的。
### 具体实现
对于每次询问,使用两重循环枚举所有 $l\leqslant l'\leqslant r'\leqslant r$,并用一重循环计算 $\displaystyle\sum_{i=l'}^{r'}a_i$。若 $\displaystyle\sum_{i=l'}^{r'}a_i\leqslant0$,则该 $(l',r')$ 一定不合题意,跳过。再使用两重循环枚举所有 $l'\leqslant x\leqslant y\lt r'$,其中再使用一重循环,计算 $\displaystyle\sum_{i=x}^ya_i$,并判断是否存在 $\displaystyle\sum_{i=x}^ya_i\gt0$。若存在,则该 $(l',r')$ 一定不合题意。计算出符合题意的 $(l',r')$ 数量,并输出。
### 时间复杂度分析
外层两重循环,内层分别有一重循环,以及两重循环嵌套一重循环,总共最多嵌套五重循环。每个循环复杂度为 $O(n)$,则回答一次询问,时间复杂度为 $O(n^5)$。
总时间复杂度 $O(qn^5)$,实测可以通过 **子任务 0** 和 **子任务 1**。
### 参考核心代码
```cpp
for(i=l;i<=r;i++)
for(j=i;j<=r;j++){//枚举所有(l',r')
long long summ=0;
for(k=i;k<=j;k++)
summ+=a[k];
if(summ<=0)//求和,若小于0不合题意
continue;
bool flg=true;
for(x=i;x<j;x++)
for(y=x;y<j;y++){//枚举区间内所有(x,y)
summ=0;
for(m=x;m<=y;m++)
summ+=a[m];
if(summ>0){//若存在[x,y]区间和大于0,则不合题意
flg=false;
break;
}
}
if(flg)
ans++;//若符合题意答案加1
...
printf("%d\n",ans);
```
### 期望得分
$15$ 分。
## 子任务 2
### 做法
对于每次询问,枚举 **所有可能的 $r'$**。若 $a_{r'}\leqslant0$,则对于所有 $(l',r')$,该数对均 **不符合题意**。否则,我们 **二分** 出一个整数位置 $p\in[1,r']$,满足 $\displaystyle\sum_{i=p}^{r'}a_i\gt0\land\forall x\in \{p,p+1,p+2,\dots,r'-1\},a_x\leqslant 0$,且 $\displaystyle\sum_{i=p-1}^{r'}a_i\leqslant0\lor\exists x\in\{p-1,p,p+1,\dots,r'-1\},a_x\gt0$。此时得到的位置 $p$ 即为以 $r'$ 为右端点的所有数对 $(l',r')$ 中,**最小的 $l'$**。我们将 $\min(r'-p+1,r'-l+1)$ 加入答案。
枚举完所有 $r'\in[l,r]$,即可求出符合题意的 $(l',r')$ 个数。
### 正确性证明
根据题意,符合题意的 $(l',r')$ 必须满足:$\forall l'\leqslant x\leqslant y\lt r'$,都有 $\displaystyle\sum_{i=x}^ya_i\leqslant0\implies\displaystyle\sum_{i=l'}^{r'-1}a_i\leqslant0$。
又知 $\displaystyle\sum_{i=l'}^{r'}a_i\gt0$,因此 $a_{r'}=\displaystyle\sum_{i=l'}^{r'}a_i-\displaystyle\sum_{i=l'}^{r'-1}a_i\gt0$。
所以,**$(l',r')$ 符合题意 $\implies a_{r'}\gt0$**。
故其逆否命题成立: **$a_{r'}\leqslant0\implies (l',r')$ 不符合题意**。
回到定义:$\forall l'\leqslant x\leqslant y\lt r'$,都有 $\displaystyle\sum_{i=x}^ya_i\leqslant0\implies\forall l'\leqslant x\lt r'$,都有 $a_x\leqslant0$。
所以,**$(l',r')$ 符合题意 $\implies\forall l'\leqslant x\lt r',a_x\leqslant0$**。
又根据定义,可以得到:**$(l',r')$ 符合题意 $\iff\displaystyle\sum_{i=l'}^{r'}a_i\gt0$**。
若 $\forall l'\leqslant x\lt r'$,都有 $a_x\leqslant 0$,则 $\forall l'\leqslant x\leqslant y\lt r'$,有 $\displaystyle\sum_{i=x}^ya_i=a_x+a_{x+1}+a_{x+2}+\cdots+a_y\leqslant0$。
综上所述:
- **一个数对 $(l',r')$ 符合题意 $\iff$ $\forall l'\leqslant x\lt r',a_x\leqslant0$ $\land$ $\displaystyle\sum_{i=l'}^{r'}a_i\gt0$**。
- **$a_{r'}\gt0$ 是数对 $(l',r')$ 符合题意的必要条件**。
对于一个确定的 $r'$,记 $f(x)=\displaystyle\sum_{i=x}^{r'-1}[a_i\gt0],x\in\{1,2,\dots,r'-1\}$,则:
$\forall x_1,x_2\in\{1,2,\dots,r'-1\},x_1\lt x_2$,都有
$$\begin{aligned}
f(x_1)-f(x_2)&=\displaystyle\sum_{i=x_1}^{r'-1}[a_i\gt0]-\displaystyle\sum_{i=x_2}^{r'-1}[a_i\gt0]\\
&=\displaystyle\sum_{i=x_1}^{x_2-1}[a_i\gt0]\geqslant0
\end{aligned}$$
即 $f(x_1)\geqslant f(x_2)$,因此 $f(x)$ 是 **非严格单调递减** 函数。
根据上述推理,$f(l')=0\implies$ 数对 $(l',r')$ 符合题意。由上述 **单调性** 证明,我们可以通过二分,找到满足 $f(l')=0$ 的最小的 $l'$。我们记这个位置为 $pos$。
对于一个确定的 $r'$ 及其对应的 $pos$,记 $g(x)=\displaystyle\sum_{i=x}^{r'}a_i,x\in\{pos,pos+1,\dots,r'\}$,则:
$\forall x_1,x_2\in\{pos,pos+1,\dots,r'\},x_1\lt x_2$,都有
$$\begin{aligned}
g(x_1)-g(x_2)&=\displaystyle\sum_{i=x_1}^{r'}a_i-\displaystyle\sum_{i=x_2}^{r'}a_i\\
&=\displaystyle\sum_{i=x_1}^{x_2-1}a_i
\end{aligned}$$
$x_1,x_2-1\in\{pos,pos+1,\dots,r'-1\}\implies\forall i\in\{x_1,x_1+1,\dots,x_2-1\},a_i\leqslant0
即 $g(x_1)\leqslant g(x_2)$,因此 $g(x)$ 是 **非严格单调递增** 函数。
根据上述推理,$g(l')\gt0\implies$ 数对 $(l',r')$ 符合题意。由上述 **单调性** 证明,我们可以通过二分,找到满足 $g(l')\gt0$ 的最小的 $l'=p$。此时我们已经满足了数对 $(l',r')$ 符合题意的充要条件。
此时我们还没有考虑到询问区间的 $l$ 和 $r$。$r'\leqslant r$ 的约束已经满足。此时我们找到的 $l'$ 若大于等于 $l$,则符合题意。否则,**根据单调性**,取询问区间的 $l$,即为满足所有约束的最小的 $l'$。
因此答案的增加量为 $\min(r'-p+1,r'-l+1)$,思路正确。
### 具体实现
我们预处理出 $sum_{1,i}=\displaystyle\sum_{j=1}^i[a_i\leqslant0]$,$sum_{2,i}=\displaystyle\sum_{j=1}^ia_i$。这一部分使用 **前缀和** 预处理。
对于每个询问,首先枚举 $r'\in[l,r]$,若 $a_{r'}\leqslant0$,则跳过该右端点。否则二分出位置 $p$:
设置初始位置左右端点 $L=1,R=r'$,当前位置中点 $mid=\lfloor\dfrac{L+R}{2}\rfloor$。判断此时的 $mid$ 是否满足:$\displaystyle\sum_{i=mid}^{r'-1}[a_i\leqslant0]=r'-mid\land\displaystyle\sum_{i=mid}^{r'}a_i\gt0$,即是否满足 $sum_{1,r'-1}-sum_{1,mid-1}=r'-mid\land sum_{2,r'}-sum_{2,mid-1}\gt0$。若 **满足**,则向 **更靠左** 的位置二分;否则,向 **更靠右** 的位置二分。
最终得到一个位置 $p$,则将答案增加 $\min(r'-p+1,r'-l+1)$。
枚举完所有的 $r'$,输出答案。
### 时间复杂度分析
预处理前缀和,复杂度 $O(n)$。对于每个询问,枚举 $r'$,复杂度为 $O(n)$;二分出一个位置,复杂度为 $O(\log n)$。
总时间复杂度 $O(qn\log n)$,可以通过 **子任务 0**、**子任务 1** 和 **子任务 2**。
### 参考核心代码
```cpp
for(i=1;i<=n;i++)
sum[i]=sum[i-1]+a[i];
for(i=1;i<=n;i++)
sum1[i]=sum1[i-1]+(a[i]<=0);//预处理两个前缀和
...
int ans=0;
for(i=l;i<=r;i++){
if(a[i]<=0)
continue;//若a[i]<=0一定不合题意
int lt=0,rt=i+1;
while(lt+1!=rt){//二分出最小的l'
int mid=(lt+rt)>>1;
if(sum[i]-sum[mid-1]>0&&sum1[i-1]-sum1[mid-1]==i-mid)//判断条件,区间和大于0且[mid,i-1]中只能有非正数
rt=mid;//向左二分
else
lt=mid;
}
ans+=min(i-rt+1,i-l+1);//注意要取min
}
printf("%d\n",ans);
```
### 期望得分
$35$ 分。
## 子任务 3
### 做法
将所有询问 **离线**,并按照左端点 $l$ **降序排序**。将端点从 $n$ 向左扫,记开始扫之前答案为 $0$。设当前扫到了位置 $i$,若 $a_i>0$,则 **答案加 $1$**;否则,记位置 $p$ 为满足 $p>i$,且 $a_p>0$ 的第一个位置,若存在位置 $p$ 且有 $\displaystyle\sum_{j=i}^pa_j>0$,则 **答案加 $1$**;否则,**答案不变**。执行上述操作后,所有左端点 $l=i$ 的询问的答案即为当前答案。
扫完整个序列后,即可求出所有询问的答案。
### 正确性证明
由于所有询问的 **右端点固定**,都为 $n$,因此我们可以只考虑左端点移动对答案的 **贡献**。由于我们可以按照左端点排序,因此我们可以只考虑左端点 **向左移动** 对答案的贡献。
记 $ans(x)$ 为区间 $[x,n]$ 的答案。当 $x$ 变为 $x-1$ 时,增加的区间有 $[x-1,x-1],[x-1,x],[x-1,x+1],\dots,[x-1,n-1],[x-1,n]$。
若 $a_{x-1}\gt0$,则有 $\forall i\in\{x,x+1,x+2,\dots,n\}$,都必然 $\exists j\in\{x-1,x,\dots,i\},a_j\gt0$,因此区间 $[x-1,x],[x-1,x+1],\dots,[x-1,n-1],[x-1,n]$ 都不符合题意(一个区间 $[l',r']$ 对应的数对 $(l',r')$ 符合题意的条件见 **子任务 2** 的证明)。由于 $a_{x-1}\gt0$,且该区间只有一个元素,故符合题意。
因此,$a_{x-1}\gt0\implies ans(x-1)=ans(x)+1$ 。
若 $a_{x-1}\leqslant0$,则 $\forall i\in\{x-1,x,\dots,p-1\}$,都有 $a_i\leqslant0$($p$ 的含义同上文),因此区间 $[x-1,x-1],[x-1,x],\dots,[x-1,p-1]$ 都 **不符合题意**(原因见 **子任务 2** 的证明)。同时,由于 $a_p\gt0$,因此区间 $[x-1,p+1],[x-1,p+2],\dots,[x-1,n]$ 都不符合题意(原因见上文)。
若还有 $\displaystyle\sum_{i=x-1}^pa_i\gt0$,则满足所有符合题意的充要条件,则区间 $[x-1,p]$ 符合题意,则此时 $ans(x-1)=ans(x)+1$。
否则,区间 $[x-1,p]$ 不合题意,则此时 $ans(x-1)=ans(x)$。
这样我们讨论完了左端点向左移动一个位置时的所有情况,因此所有左端点为当前左端点的询问的答案即为当前答案,故 **思路正确**。
### 具体实现
记录所有询问的左端点及询问编号,使用 **计数排序** 的方式将所有左端点相同的询问编号挂在同一个左端点上。**这里使用 `vector` 实现**。
设置端点为 $n$,将该端点从 $n$ 向 $1$ 位置扫。设当前扫到了位置 $i$。首先判断 $a_i\gt0$ 是否成立,若成立,答案加 $1$。否则,我们 **找到** 在位置 $i$ 之后的第一个位置 $p$ 满足 $a_p\gt0$,并使用前缀和判断区间 $[i,p]$ 的元素和是否大于 $0$。若是,答案加 $1$;否则,答案不变。
我们 **动态更新** 当前在位置 $i$ 之后第一个 $a_p\gt0$ 的位置 $p$。初始 $p=n+1$,若当前位置 $i$ 的 $a_i\gt0$,则令 $p\leftarrow i$。
回答左端点为 $i$ 的所有询问,并存在对应询问编号的答案中。扫完整个序列,按编号顺次输出每个询问的答案。
### 时间复杂度分析
将询问离线并降序排序,按上述实现,复杂度为 $O(q)$。从右往左扫一遍复杂度 $O(n)$。每个位置,可以 $O(1)$ 找到位置 $p$,故计算一个询问的答案,复杂度为 $O(1)$。
总时间复杂度 $O(n+q)$,可以通过 **子任务 3**。
### 参考核心代码
```cpp
vector<int>ask[N];
...
for(i=1;i<=q;i++){
int l=R(),r=R();
ask[l].push_back(i);//将询问编号i放入询问左端点为l的vector中,相当于计数排序
}
int ans=0,now=n+1;//now记录当前位置右边第一个a[p]>=0的位置p
for(i=n;i>=1;i--){
if(a[i]>0){
ans++;
now=i;//若a[i]>0答案加1,并更新now
}
else if(now!=n+1)//now位置存在
if(sum[now]-sum[i-1]>0)//判断符合题意
ans++;
for(auto qid:ask[i])
anss[qid]=ans;//回答询问
}
for(i=1;i<=q;i++)
printf("%d\n",anss[i]);
```
### 期望得分
$15$ 分。结合 **子任务 0** 的解法可以获得 $15$ 分,结合 **子任务 1** 的解法可以获得 $30$ 分,结合 **子任务 2** 的解法可以获得 $50$ 分。
## 子任务 4
### 做法
将所有询问离线,并按照右端点 $r$ **升序排序**。将端点从 $1$ 向右扫,设开始扫之前答案为 $0$。设当前扫到的位置为 $i$,若 $a_i\leqslant0$,则答案不变。否则,二分出位置 $p$,满足 $\displaystyle\sum_{i=p}^{r'}a_i\gt0\land\forall x\in \{p,p+1,p+2,\dots,r'-1\},a_x\leqslant 0$,且 $\displaystyle\sum_{i=p-1}^{r'}a_i\leqslant0\lor\exists x\in\{p-1,p,p+1,\dots,r'-1\},a_x\gt0$,将 $i-p+1$ 加入答案。执行完上述操作后,当前答案即为所有 $r=i$ 的询问的答案。
### 正确性证明
由于 **左端点固定**,均为 $1$,且我们可以按照右端点将询问排序,因此我们只需要考虑右端点 **右移一个位置** 对答案的贡献。
可以发现,右端点右移一个位置时,增加的所有区间的右端点 **相同**。这就转化为:**固定右端点 $r'$,求所有符合题意的数对 $(l',r')$ 个数**。该问题与 **子任务 2** 中的问题相同,因此解法也与那一问题相同,具体见 **子任务 2**。注意到此时必有 $l=1$,因此二分得到的位置 $p$ 一定是符合区间左端点约束的,即必有 $p\geqslant1$。因此将 $i-p+1$ 加入答案即可。
我们正确解决了右端点右移一个位置对答案的贡献问题,因此该思路是正确的。
### 具体实现
使用 `vector` 实现对询问的按右端点排序。设置端点为 $1$,从 $1$ 扫到 $n$。设当前扫到的位置为 $i$。首先判断 $a_i\gt0$ 是否成立,若不成立,答案不变。否则,二分出上述位置 $p$,具体方法见 **子任务 2**。答案增加 $i-p+1$。回答右端点为 $i$ 的所有询问,将答案存入对应的询问编号。
最后按照询问编号顺次输出答案。
### 时间复杂度分析
按照上述实现,对询问排序是 $O(n)$ 的。从左往右扫,每次可能需要二分,单次复杂度 $O(\log n)$,回答询问总复杂度为 $O(q)$。
总时间复杂度 $O(n\log n)$,可以通过 **子任务 4**。
### 参考核心代码
```cpp
for(i=1;i<=q;i++){
int l=R(),r=R();
ask[r].push_back(i);//将询问按右端点升序排序
}
int ans=0;
for(i=1;i<=n;i++){
if(a[i]>0){//只有a[i]>0才可能更新答案
int lt=0,rt=i+1;
while(lt+1!=rt){
int mid=(lt+rt)>>1;
if(sum[i]-sum[mid-1]>0&&sum1[i-1]-sum1[mid-1]==i-mid)
rt=mid;
else
lt=mid;
}
ans+=i-rt+1;//二分出位置p
}
for(auto qid:ask[i])
anss[qid]=ans;//回答询问
}
for(i=1;i<=q;i++)
printf("%d\n",anss[i]);
```
### 期望得分
$15$ 分。结合 **子任务 0** 的解法可以获得 $15$ 分,结合 **子任务 1** 的解法可以获得 $30$ 分,结合 **子任务 3** 的解法可以获得 $30$ 分,结合 **子任务 0** 和 **子任务 3** 的解法可以获得 $30$ 分,结合 **子任务 1** 和 **子任务 3** 的解法可以获得 $45$ 分,结合 **子任务 2** 的解法可以获得 $50$ 分,结合 **子任务 2** 和 **子任务 3** 的解法可以获得 $65$ 分。
## 子任务 5
### 做法
对于每个位置 $i$,使用二分法,**预处理** 出以 $i$ 为右端点 $i=r'$ 的所有符合题意的数对 $(l',r')$ 个数 $num_i$(具体方法见 **子任务 2**)。对于一个询问 $(l,r)$,设区间 $[l,r]$ 中最小的位置 $p$,满足 $a_p\gt0$,求出 $\min(num_p,p-l+1)+\displaystyle\sum_{i=p+1}^rnum_i$,即为该询问的答案。
### 正确性证明
由于原序列 $a$ **不发生改变**,因此我们可以预处理出上述的 $num_i$(正确性证明见 **子任务 2**)。
设一个位置 $i$ 满足 $a_i\gt0$,位置 $las$ 也满足 $a_{las}\gt0$,且有 $\forall j\in\{las+1,las+2,\dots,i-1\}$。都有 $a_j\leqslant0$,设对于位置 $i$,二分出的位置为 $pos$。根据 **子任务 2** 中的推断,假设 $pos\leqslant las$,则 $\exists j\in\{pos,pos+1,\dots,i-1\}$,满足 $a_j\gt0$,则区间 $[pos,i]$ 不合法,这与 **$pos$ 为满足区间 $[l',i]$ 合法的最小的 $l'$** 这一结论相矛盾。因此假设不成立,故 $pos\gt las$。
对于一个询问 $(l,r)$,设其中所有满足 $a_i\gt0$ 的所有 $i$ 从小到大分别为 $i_1,i_2,\dots,i_k$,其中 $k=\displaystyle\sum_{i=l}^r[a_i\gt0]$。该询问的答案 $ans=\displaystyle\sum_{i=l}^r\min(i-l+1,num_i)$。对于 $a_i\leqslant0$ 的位置 $i$,$num_i=0$,因此 $\min(i-l+1,num_i)=num_i$。对于 $j\in\{2,3,\dots,k\}$,根据上述推理,**必有对应的 $p\gt i_{j-1}\geqslant l$**,因此 $num_{i_j}\lt i_j-l+1\implies \min(i_j-l+1,num_{i_j})=num_{i_j}$。**只有 $i_1$ 位置的 $i_1-l+1$ 与 $num_{i_1}$ 大小关系不确定,需要单独计算**。
**即证**:$ans=\min(p-l+1,num_p)+\displaystyle\sum_{i=p+1}^rnum_i$,其中 $p$ 的含义与本子任务“做法”中 $p$ 含义相同。
因此该思路正确。
### 具体实现
首先预处理出 $num_i$ 数组,具体方法见 **子任务 2**。根据上述证明,我们也可以动态记录每个数左边第一个元素值大于 $0$ 的位置,将二分左端点设置为上一个满足 $a_i\gt0$ 的位置 $i$,实现一个小优化。
预处理出 $num_i$ 数组的 **前缀和** 数组 $sumn_i$,预处理出每个数 **右边** 第一个元素值大于 $0$ 的位置。对于一个询问,直接计算 $sumn_r-sumn_{l-1}$,并找到 $l-1$ 右边第一个元素值大于 $0$ 的位置 $x$,减去 $num_x$,并加上 $\min(num_x,x-l+1)$,即为该询问的答案,输出即可。
### 时间复杂度分析
预处理 $num_i$ 数组,单次处理复杂度 $O(\log n)$,总复杂度 $O(n\log n)$;预处理 $sumn_i$ 数组与每个数右边第一个元素值大于 $0$ 的位置,复杂度都是 $O(n)$。单次处理询问,计算、查找位置,复杂度都为 $O(1)$,总复杂度 $O(q)$。
总时间复杂度 $O(n\log n)$,可以通过 **本题**。
### 参考核心代码
```cpp
int now=0;
for(i=1;i<=n;i++){
lef[i]=now;//前面第一个正数位置
if(a[i]>0)
now=i;
}
now=n+1;
for(i=n;i>=1;i--){
rig[i]=now;//后面第一个正数位置
if(a[i]>0)
now=i;
}
...(预处理num数组及前缀和)
int l=R(),r=R(),pos=rig[l-1];//找到l-1右边第一个正数的位置
if(pos>r){//注意这里要特判,否则会使得找到的pos越界导致错误
puts("0");
continue;
}
printf("%d\n",sumn[r]-sumn[l-1]-num[pos]+min(num[pos],pos-l+1));//根据上述式子计算答案
```
### 期望得分
$100$ 分。