1 条题解

  • 0
    @ 2025-8-24 23:17:45

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar lcfollower
    不 AK Div.2 & 3 不改签 | AT 不上蓝不改签(目前只有绿)。 | 不拿蓝钩不改签。 | 大号在 N 个月前已清关,误清的用这个号回关

    搬运于2025-08-24 23:17:45,当前版本为作者最后更新于2025-06-10 21:03:02,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    修改

    • 2025.7.312025.7.31:根据评论修改一处错误(自己懒得写对应部分分代码犯下了唐错误),并且顺带修改一点错别字和写错的值域范围。

    这题其实一眼 DP,但是可能状态设计需要细想才能优化。

    子任务 1122

    这里的 a,ba ,b 很小,T=1T = 1

    于是我们设 fi,j,kf_{i ,j, k} 为前 ii 个环,红色的和恰好为 jj,蓝色的和恰好为 kk 的方案数。

    • 边界条件:f0,0,0=1f_{0 ,0 ,0} = 1

    • 状态转移:$f_{i ,j ,k} = f_{i - 1 ,j - i ,k} + f_{i - 1 ,j ,k - i}$。

    • 其中,fi1,ji,kf_{i - 1 ,j - i ,k} 表示第 ii 圈涂红的方案数,fi1,j,kif_{i - 1 ,j ,k - i} 表示第 ii 圈涂蓝的方案数。

    • 注意边界!比如 jij \ge ikik\ge i 才能转移之类的。

    • s=(1+i)i2s = \frac{(1 + i)i}{2},即前 ii 圈数值的和,又可以理解为红色与蓝色的和(正好涂满整个所有环)。

    • 转移条件:j+k=sj + k = s,即正好涂满。

    这样做时间复杂度为 O(nV2)\mathcal O(nV^2),其中 nn 为圈的个数,VVaabb 的值域(即 5×1045\times 10^4)。

    但是观察到,转移条件为 j+k=sj + k = s,我们可以在 [0,s][0,s] 枚举 jj,然后通过 k=sjk = s - j 求的 kk

    这样的时间复杂度为 O(nV)\mathcal O(nV)

    那么答案即为 $\sum\limits_{i = 1}^{n}\sum\limits_{j=0}^a [s-j\le b]\times f_{i,j,s-j}$。

    其中,[A][A] 表示 AA 成立,值为 11;否则为 00

    ss,表示 (1+i)i2\frac{(1+i)i}{2},下文都这么表示,不再阐述。

    这样求答案也是 O(nV)\mathcal O(nV) 的,足以通过两个子任务,总时间复杂度为 O(TnV)\mathcal O(TnV),理论上来说可以做到 9090

    这里埋了一个点,nn 的范围?

    考虑到 a+bsa+b\ge s,不然整个圈都涂不满。

    因为 s=i(i+1)2s = \frac{i(i+1)}{2}a+b105a+b\le 10^5

    所以 ss 约为 2×105=447\lfloor\sqrt{2\times 10^5}\rfloor = 447,数组开 450450 包不会缺少圈数,更何况 447447 还是估大了。

    但是过不了更高的分数是因为空间问题。

    代码这里不放了,可以自己练练手。

    空间优化

    考虑到 fi,j,kf_{i,j,k} 只有在 j+k=sj+k=s 时才能转移,那么我们能不能:

    • 状态设计:fi,jf_{i,j} 为前 ii 圈涂红的和恰好为 jj,涂蓝的和恰好为 sjs - j 的方案数。

    这样明显是可以的!

    那么:

    • 边界条件:f0,0=1f_{0 ,0} = 1

    • 状态转移:fi,j=fi1,j+fi1,jif_{i ,j} = f_{i - 1,j} + f_{i - 1 ,j - i}

    • 其中,fi1,jf_{i - 1,j} 表示第 ii 圈涂蓝的方案数,fi1,jif_{i - 1 ,j - i} 表示第 ii 圈涂红的方案数。

    • 注意边界!比如 jij\ge i 才能转移之类的。

    这样空间复杂度变成了 O(nV)\mathcal O(nV)!即为 O(VV)\mathcal O(V\sqrt{V}),非常优秀!

    于是我们拿到了 9090 分!

    inline void solve (){
      int x = read () ,y = read () ,ans = 0;
      up (i ,1 ,450) {
        int s = (1 + i) * i / 2;
        up (j ,s - y ,x) ans += f[i][j] ,ans %= mod;
    //在这个范围内可以保证 s - j <= y,可以自己解不等式试试。
      } writeln (ans);
    } inline void init (){
      f[0][0] = 1;//边界条件。
      up (i ,1 ,450){
        int s = (1 + i) * i / 2;
        up (j ,0 ,50000){
          int k = s - j;
          if (k < 0) break;//蓝的不可能涂色,不用转移。
          if (j >= i) f[i][j] = (f[i - 1][j] + f[i - 1][j - i]) % mod;//这一圈可以涂红,也可以涂蓝。
          else f[i][j] = f[i - 1][j];//不然这一圈这能涂蓝。
        }
      }
    }
    

    正解

    观察到我们的瓶颈在于 solve\operatorname {solve} 中,考虑优化它。

    我们发现这是一段和的形式,于是我们想到用前缀和,这样时间复杂度为 O(Tn)\mathcal O(Tn) 了。

    但是前缀和还有边界,现在我们记 fi,x=j=1xfi,jf'_{i,x} = \sum\limits_{j=1}^x f_{i,j}

    • 首先要满足 syxs-y\le x,不然不能转移。

    • sy0s - y \le 0,那么贡献为 fi,xf'_{i,x}

    • 否则,贡献为 fi,xfi,sy1f'_{i,x} - f'_{i,s-y-1}

    所有贡献之和即为答案,注意取模!


    修改过后的核心部分:

    inline void solve (){
      int x = read () ,y = read () ,ans = 0;
      up (i ,1 ,450) {
        int s = (1 + i) * i / 2;
        int p = max (0ll ,s - y - 1);
        if (s - y > x) break;//不能转移,j 更大时肯定也不能转移。
        if (s - y <= 0) ans = (ans + f[i][x]) % mod;
        else ans += f[i][x] - f[i][p] + mod ,ans %= mod;//两种边界,防止 RE。
      } writeln (ans);
    }
    

    完整代码提交记录搓这里。

    最后

    求通过并无耻地求赞。

    • 1

    信息

    ID
    12462
    时间
    2000ms
    内存
    1024MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者