1 条题解

  • 0
    @ 2025-8-24 22:50:42

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar HFanGDoDM
    热夏你归来听蝉,再游于北方知寒。沿途不枉为少年,终有个结局圆满。

    搬运于2025-08-24 22:50:42,当前版本为作者最后更新于2023-10-07 00:11:06,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前置知识

    二分法

    题意简述

    给定一个长度为 nn 的序列 aaqq 次询问,每次询问给定 l,rl,r,求满足以下条件的数对 (l,r)(l',r') 个数。

    • llrrl\leqslant l'\leqslant r'\leqslant r

    • i=lrai>0\displaystyle\sum_{i=l'}^{r'}a_i\gt 0

    • $\forall x,y,l'\leqslant x\leqslant y\lt r',\displaystyle\sum_{i=x}^ya_i\leqslant 0$。

    数据范围

    子任务 0

    n{4,7}n\in\{4,7\}q{3,6}q\in\{3,6\}1l51\leqslant l\leqslant 5r{2,3,4,5,7}r\in\{2,3,4,5,7\}1lrn1\leqslant l\leqslant r\leqslant nai{4,1,2,3}a_i\in\{-4,-1,2,3\}

    子任务 1

    1n,q501\leqslant n,q\leqslant 501lrn1\leqslant l\leqslant r\leqslant n109ai109-10^9\leqslant a_i\leqslant 10^9

    子任务 2

    1n,q30001\leqslant n,q\leqslant 30001lrn1\leqslant l\leqslant r\leqslant n109ai109-10^9\leqslant a_i\leqslant 10^9

    子任务 3

    1n,q2×1051\leqslant n,q\leqslant 2\times 10^51lr=n1\leqslant l\leqslant r=n109ai109-10^9\leqslant a_i\leqslant 10^9

    子任务 4

    1n,q2×1051\leqslant n,q\leqslant 2\times 10^51=lrn1=l\leqslant r\leqslant n109ai109-10^9\leqslant a_i\leqslant 10^9

    子任务 5

    1n,q2×1051\leqslant n,q\leqslant 2\times 10^51lrn1\leqslant l\leqslant r\leqslant n109ai109-10^9\leqslant a_i\leqslant 10^9

    解题思路

    子任务 0

    做法

    n=4,q=3n=4,q=3,则答案分别为 1,2,31,2,3;若 n=7,q=6n=7,q=6,则答案分别为 1,2,4,0,2,11,2,4,0,2,1

    正确性证明

    n=4,q=3n=4,q=3 的答案在 样例输出 #1 中给出,分别为 1,2,31,2,3n=7,q=6n=7,q=6 的答案在 样例输出 #2 中给出,分别为 1,2,4,0,2,11,2,4,0,2,1

    具体实现

    判断 n,qn,q 的值。若 n=4,q=3n=4,q=3,则输出三行三个整数,分别为 1,2,31,2,3;若 n=7,q=6n=7,q=6,则输出六行六个整数,分别为 1,2,4,0,2,11,2,4,0,2,1

    时间复杂度分析

    输出一次,复杂度为 O(1)O(1),可以通过 子任务 0

    参考核心代码

    if(n==4&&q==3)
    	printf("1\n2\n3");
    if(n==7&&q==6)
    	printf("1\n2\n4\n0\n2\n1");
    

    期望得分

    00 分。

    子任务 1

    做法

    对于每次询问,枚举所有可能的 (l,r)(l',r')。根据定义,检查 i=lrai\displaystyle\sum_{i=l'}^{r'}a_i 是否大于 00。若 i=lrai0\displaystyle\sum_{i=l'}^{r'}a_i\leqslant0,则该 (l,r)(l',r') 不符合题意。并枚举该区间内所有可能的 xxyy,判断是否有 $l'\leqslant x\leqslant y\lt r',\displaystyle\sum_{i=x}^ya_i\leqslant0$。若存在一个 (x,y)(x,y),满足 i=xyai>0\displaystyle\sum_{i=x}^ya_i\gt0,则该 (l,r)(l',r') 不符合题意。

    最后可以求出所有符合题意的 (l,r)(l',r') 个数。

    正确性证明

    由于我们每次枚举了 所有可能的 (l,r)(l',r'),并按照定义,逐一检查了 (l,r)(l',r') 是否符合题意,因此该思路是正确的。

    具体实现

    对于每次询问,使用两重循环枚举所有 llrrl\leqslant l'\leqslant r'\leqslant r,并用一重循环计算 i=lrai\displaystyle\sum_{i=l'}^{r'}a_i。若 i=lrai0\displaystyle\sum_{i=l'}^{r'}a_i\leqslant0,则该 (l,r)(l',r') 一定不合题意,跳过。再使用两重循环枚举所有 lxy<rl'\leqslant x\leqslant y\lt r',其中再使用一重循环,计算 i=xyai\displaystyle\sum_{i=x}^ya_i,并判断是否存在 i=xyai>0\displaystyle\sum_{i=x}^ya_i\gt0。若存在,则该 (l,r)(l',r') 一定不合题意。计算出符合题意的 (l,r)(l',r') 数量,并输出。

    时间复杂度分析

    外层两重循环,内层分别有一重循环,以及两重循环嵌套一重循环,总共最多嵌套五重循环。每个循环复杂度为 O(n)O(n),则回答一次询问,时间复杂度为 O(n5)O(n^5)

    总时间复杂度 O(qn5)O(qn^5),实测可以通过 子任务 0子任务 1

    参考核心代码

    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);
    

    期望得分

    1515 分。

    子任务 2

    做法

    对于每次询问,枚举 所有可能的 rr'。若 ar0a_{r'}\leqslant0,则对于所有 (l,r)(l',r'),该数对均 不符合题意。否则,我们 二分 出一个整数位置 p[1,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$。此时得到的位置 pp 即为以 rr' 为右端点的所有数对 (l,r)(l',r') 中,最小的 ll'。我们将 min(rp+1,rl+1)\min(r'-p+1,r'-l+1) 加入答案。

    枚举完所有 r[l,r]r'\in[l,r],即可求出符合题意的 (l,r)(l',r') 个数。

    正确性证明

    根据题意,符合题意的 (l,r)(l',r') 必须满足:lxy<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$。

    又知 i=lrai>0\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)(l',r') 符合题意     ar>0\implies a_{r'}\gt0

    故其逆否命题成立: ar0    (l,r)a_{r'}\leqslant0\implies (l',r') 不符合题意

    回到定义:lxy<r\forall l'\leqslant x\leqslant y\lt r',都有 $\displaystyle\sum_{i=x}^ya_i\leqslant0\implies\forall l'\leqslant x\lt r'$,都有 ax0a_x\leqslant0

    所以,(l,r)(l',r') 符合题意     lx<r,ax0\implies\forall l'\leqslant x\lt r',a_x\leqslant0

    又根据定义,可以得到:(l,r)(l',r') 符合题意     i=lrai>0\iff\displaystyle\sum_{i=l'}^{r'}a_i\gt0

    lx<r\forall l'\leqslant x\lt r',都有 ax0a_x\leqslant 0,则 lxy<r\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)(l',r') 符合题意     \iff lx<r,ax0\forall l'\leqslant x\lt r',a_x\leqslant0 \land i=lrai>0\displaystyle\sum_{i=l'}^{r'}a_i\gt0

    • ar>0a_{r'}\gt0 是数对 (l,r)(l',r') 符合题意的必要条件

    对于一个确定的 rr',记 $f(x)=\displaystyle\sum_{i=x}^{r'-1}[a_i\gt0],x\in\{1,2,\dots,r'-1\}$,则:

    x1,x2{1,2,,r1},x1<x2\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(x1)f(x2)f(x_1)\geqslant f(x_2),因此 f(x)f(x)非严格单调递减 函数。

    根据上述推理,f(l)=0    f(l')=0\implies 数对 (l,r)(l',r') 符合题意。由上述 单调性 证明,我们可以通过二分,找到满足 f(l)=0f(l')=0 的最小的 ll'。我们记这个位置为 pospos

    对于一个确定的 rr' 及其对应的 pospos,记 $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(x1)g(x2)0\implies g(x_1)-g(x_2)\leqslant0

    g(x1)g(x2)g(x_1)\leqslant g(x_2),因此 g(x)g(x)非严格单调递增 函数。

    根据上述推理,g(l)>0    g(l')\gt0\implies 数对 (l,r)(l',r') 符合题意。由上述 单调性 证明,我们可以通过二分,找到满足 g(l)>0g(l')\gt0 的最小的 l=pl'=p。此时我们已经满足了数对 (l,r)(l',r') 符合题意的充要条件。

    此时我们还没有考虑到询问区间的 llrrrrr'\leqslant r 的约束已经满足。此时我们找到的 ll' 若大于等于 ll,则符合题意。否则,根据单调性,取询问区间的 ll,即为满足所有约束的最小的 ll'

    因此答案的增加量为 min(rp+1,rl+1)\min(r'-p+1,r'-l+1),思路正确。

    具体实现

    我们预处理出 sum1,i=j=1i[ai0]sum_{1,i}=\displaystyle\sum_{j=1}^i[a_i\leqslant0]sum2,i=j=1iaisum_{2,i}=\displaystyle\sum_{j=1}^ia_i。这一部分使用 前缀和 预处理。

    对于每个询问,首先枚举 r[l,r]r'\in[l,r],若 ar0a_{r'}\leqslant0,则跳过该右端点。否则二分出位置 pp

    设置初始位置左右端点 L=1,R=rL=1,R=r',当前位置中点 mid=L+R2mid=\lfloor\dfrac{L+R}{2}\rfloor。判断此时的 midmid 是否满足:$\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$。若 满足,则向 更靠左 的位置二分;否则,向 更靠右 的位置二分。

    最终得到一个位置 pp,则将答案增加 min(rp+1,rl+1)\min(r'-p+1,r'-l+1)

    枚举完所有的 rr',输出答案。

    时间复杂度分析

    预处理前缀和,复杂度 O(n)O(n)。对于每个询问,枚举 rr',复杂度为 O(n)O(n);二分出一个位置,复杂度为 O(logn)O(\log n)

    总时间复杂度 O(qnlogn)O(qn\log n),可以通过 子任务 0子任务 1子任务 2

    参考核心代码

    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);
    

    期望得分

    3535 分。

    子任务 3

    做法

    将所有询问 离线,并按照左端点 ll 降序排序。将端点从 nn 向左扫,记开始扫之前答案为 00。设当前扫到了位置 ii,若 ai>0a_i>0,则 答案加 11;否则,记位置 pp 为满足 p>ip>i,且 ap>0a_p>0 的第一个位置,若存在位置 pp 且有 j=ipaj>0\displaystyle\sum_{j=i}^pa_j>0,则 答案加 11;否则,答案不变。执行上述操作后,所有左端点 l=il=i 的询问的答案即为当前答案。

    扫完整个序列后,即可求出所有询问的答案。

    正确性证明

    由于所有询问的 右端点固定,都为 nn,因此我们可以只考虑左端点移动对答案的 贡献。由于我们可以按照左端点排序,因此我们可以只考虑左端点 向左移动 对答案的贡献。

    ans(x)ans(x) 为区间 [x,n][x,n] 的答案。当 xx 变为 x1x-1 时,增加的区间有 $[x-1,x-1],[x-1,x],[x-1,x+1],\dots,[x-1,n-1],[x-1,n]$。

    ax1>0a_{x-1}\gt0,则有 i{x,x+1,x+2,,n}\forall i\in\{x,x+1,x+2,\dots,n\},都必然 j{x1,x,,i},aj>0\exists j\in\{x-1,x,\dots,i\},a_j\gt0,因此区间 [x1,x],[x1,x+1],,[x1,n1],[x1,n][x-1,x],[x-1,x+1],\dots,[x-1,n-1],[x-1,n] 都不符合题意(一个区间 [l,r][l',r'] 对应的数对 (l,r)(l',r') 符合题意的条件见 子任务 2 的证明)。由于 ax1>0a_{x-1}\gt0,且该区间只有一个元素,故符合题意。

    因此,ax1>0    ans(x1)=ans(x)+1a_{x-1}\gt0\implies ans(x-1)=ans(x)+1

    ax10a_{x-1}\leqslant0,则 i{x1,x,,p1}\forall i\in\{x-1,x,\dots,p-1\},都有 ai0a_i\leqslant0pp 的含义同上文),因此区间 [x1,x1],[x1,x],,[x1,p1][x-1,x-1],[x-1,x],\dots,[x-1,p-1]不符合题意(原因见 子任务 2 的证明)。同时,由于 ap>0a_p\gt0,因此区间 [x1,p+1],[x1,p+2],,[x1,n][x-1,p+1],[x-1,p+2],\dots,[x-1,n] 都不符合题意(原因见上文)。

    若还有 i=x1pai>0\displaystyle\sum_{i=x-1}^pa_i\gt0,则满足所有符合题意的充要条件,则区间 [x1,p][x-1,p] 符合题意,则此时 ans(x1)=ans(x)+1ans(x-1)=ans(x)+1

    否则,区间 [x1,p][x-1,p] 不合题意,则此时 ans(x1)=ans(x)ans(x-1)=ans(x)

    这样我们讨论完了左端点向左移动一个位置时的所有情况,因此所有左端点为当前左端点的询问的答案即为当前答案,故 思路正确

    具体实现

    记录所有询问的左端点及询问编号,使用 计数排序 的方式将所有左端点相同的询问编号挂在同一个左端点上。这里使用 vector 实现

    设置端点为 nn,将该端点从 nn11 位置扫。设当前扫到了位置 ii。首先判断 ai>0a_i\gt0 是否成立,若成立,答案加 11。否则,我们 找到 在位置 ii 之后的第一个位置 pp 满足 ap>0a_p\gt0,并使用前缀和判断区间 [i,p][i,p] 的元素和是否大于 00。若是,答案加 11;否则,答案不变。

    我们 动态更新 当前在位置 ii 之后第一个 ap>0a_p\gt0 的位置 pp。初始 p=n+1p=n+1,若当前位置 iiai>0a_i\gt0,则令 pip\leftarrow i

    回答左端点为 ii 的所有询问,并存在对应询问编号的答案中。扫完整个序列,按编号顺次输出每个询问的答案。

    时间复杂度分析

    将询问离线并降序排序,按上述实现,复杂度为 O(q)O(q)。从右往左扫一遍复杂度 O(n)O(n)。每个位置,可以 O(1)O(1) 找到位置 pp,故计算一个询问的答案,复杂度为 O(1)O(1)

    总时间复杂度 O(n+q)O(n+q),可以通过 子任务 3

    参考核心代码

    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]);
    

    期望得分

    1515 分。结合 子任务 0 的解法可以获得 1515 分,结合 子任务 1 的解法可以获得 3030 分,结合 子任务 2 的解法可以获得 5050 分。

    子任务 4

    做法

    将所有询问离线,并按照右端点 rr 升序排序。将端点从 11 向右扫,设开始扫之前答案为 00。设当前扫到的位置为 ii,若 ai0a_i\leqslant0,则答案不变。否则,二分出位置 pp,满足 $\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$,将 ip+1i-p+1 加入答案。执行完上述操作后,当前答案即为所有 r=ir=i 的询问的答案。

    正确性证明

    由于 左端点固定,均为 11,且我们可以按照右端点将询问排序,因此我们只需要考虑右端点 右移一个位置 对答案的贡献。

    可以发现,右端点右移一个位置时,增加的所有区间的右端点 相同。这就转化为:固定右端点 rr',求所有符合题意的数对 (l,r)(l',r') 个数。该问题与 子任务 2 中的问题相同,因此解法也与那一问题相同,具体见 子任务 2。注意到此时必有 l=1l=1,因此二分得到的位置 pp 一定是符合区间左端点约束的,即必有 p1p\geqslant1。因此将 ip+1i-p+1 加入答案即可。

    我们正确解决了右端点右移一个位置对答案的贡献问题,因此该思路是正确的。

    具体实现

    使用 vector 实现对询问的按右端点排序。设置端点为 11,从 11 扫到 nn。设当前扫到的位置为 ii。首先判断 ai>0a_i\gt0 是否成立,若不成立,答案不变。否则,二分出上述位置 pp,具体方法见 子任务 2。答案增加 ip+1i-p+1。回答右端点为 ii 的所有询问,将答案存入对应的询问编号。

    最后按照询问编号顺次输出答案。

    时间复杂度分析

    按照上述实现,对询问排序是 O(n)O(n) 的。从左往右扫,每次可能需要二分,单次复杂度 O(logn)O(\log n),回答询问总复杂度为 O(q)O(q)

    总时间复杂度 O(nlogn)O(n\log n),可以通过 子任务 4

    参考核心代码

    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]);
    

    期望得分

    1515 分。结合 子任务 0 的解法可以获得 1515 分,结合 子任务 1 的解法可以获得 3030 分,结合 子任务 3 的解法可以获得 3030 分,结合 子任务 0子任务 3 的解法可以获得 3030 分,结合 子任务 1子任务 3 的解法可以获得 4545 分,结合 子任务 2 的解法可以获得 5050 分,结合 子任务 2子任务 3 的解法可以获得 6565 分。

    子任务 5

    做法

    对于每个位置 ii,使用二分法,预处理 出以 ii 为右端点 i=ri=r' 的所有符合题意的数对 (l,r)(l',r') 个数 numinum_i(具体方法见 子任务 2)。对于一个询问 (l,r)(l,r),设区间 [l,r][l,r] 中最小的位置 pp,满足 ap>0a_p\gt0,求出 min(nump,pl+1)+i=p+1rnumi\min(num_p,p-l+1)+\displaystyle\sum_{i=p+1}^rnum_i,即为该询问的答案。

    正确性证明

    由于原序列 aa 不发生改变,因此我们可以预处理出上述的 numinum_i(正确性证明见 子任务 2)。

    设一个位置 ii 满足 ai>0a_i\gt0,位置 laslas 也满足 alas>0a_{las}\gt0,且有 j{las+1,las+2,,i1}\forall j\in\{las+1,las+2,\dots,i-1\}。都有 aj0a_j\leqslant0,设对于位置 ii,二分出的位置为 pospos。根据 子任务 2 中的推断,假设 poslaspos\leqslant las,则 j{pos,pos+1,,i1}\exists j\in\{pos,pos+1,\dots,i-1\},满足 aj>0a_j\gt0,则区间 [pos,i][pos,i] 不合法,这与 pospos 为满足区间 [l,i][l',i] 合法的最小的 ll' 这一结论相矛盾。因此假设不成立,故 pos>laspos\gt las

    对于一个询问 (l,r)(l,r),设其中所有满足 ai>0a_i\gt0 的所有 ii 从小到大分别为 i1,i2,,iki_1,i_2,\dots,i_k,其中 k=i=lr[ai>0]k=\displaystyle\sum_{i=l}^r[a_i\gt0]。该询问的答案 ans=i=lrmin(il+1,numi)ans=\displaystyle\sum_{i=l}^r\min(i-l+1,num_i)。对于 ai0a_i\leqslant0 的位置 iinumi=0num_i=0,因此 min(il+1,numi)=numi\min(i-l+1,num_i)=num_i。对于 j{2,3,,k}j\in\{2,3,\dots,k\},根据上述推理,必有对应的 p>ij1lp\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}$。只有 i1i_1 位置的 i1l+1i_1-l+1numi1num_{i_1} 大小关系不确定,需要单独计算

    即证:$ans=\min(p-l+1,num_p)+\displaystyle\sum_{i=p+1}^rnum_i$,其中 pp 的含义与本子任务“做法”中 pp 含义相同。

    因此该思路正确。

    具体实现

    首先预处理出 numinum_i 数组,具体方法见 子任务 2。根据上述证明,我们也可以动态记录每个数左边第一个元素值大于 00 的位置,将二分左端点设置为上一个满足 ai>0a_i\gt0 的位置 ii,实现一个小优化。

    预处理出 numinum_i 数组的 前缀和 数组 sumnisumn_i,预处理出每个数 右边 第一个元素值大于 00 的位置。对于一个询问,直接计算 sumnrsumnl1sumn_r-sumn_{l-1},并找到 l1l-1 右边第一个元素值大于 00 的位置 xx,减去 numxnum_x,并加上 min(numx,xl+1)\min(num_x,x-l+1),即为该询问的答案,输出即可。

    时间复杂度分析

    预处理 numinum_i 数组,单次处理复杂度 O(logn)O(\log n),总复杂度 O(nlogn)O(n\log n);预处理 sumnisumn_i 数组与每个数右边第一个元素值大于 00 的位置,复杂度都是 O(n)O(n)。单次处理询问,计算、查找位置,复杂度都为 O(1)O(1),总复杂度 O(q)O(q)

    总时间复杂度 O(nlogn)O(n\log n),可以通过 本题

    参考核心代码

    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));//根据上述式子计算答案
    

    期望得分

    100100 分。

    • 1

    信息

    ID
    8946
    时间
    1000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者