1 条题解

  • 0
    @ 2025-8-24 22:24:28

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar lnzwz
    **

    搬运于2025-08-24 22:24:28,当前版本为作者最后更新于2020-10-06 20:34:33,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    这题我的方法比较奇怪。

    题意:

    kk种物品,第ii个物品有aia_i个,权值为2i2^i

    求有多少个yy,使得可以选出xx组物品,每组的和都为yy

    先考虑如何判定一个yy是否可行:

    从最高位开始,依次求出第i位需要的数目bib_i。若yy的第ii位为1,则bb+xb\leftarrow b+x

    如果biaib_i \leq a_i,那么说明aia_i够用,进入下一位。

    如果bi>aib_i>a_i,则选上所有aia_i后,还剩biaib_i-a_i个。那么这些2i2^i只能用两倍的2i12^{i-1}来凑。因此把bi1b_{i-1}加上2×(biai)2 \times (b_i-a_i)

    这样,只要b0<a0b_0<a_0,则说明这个yy可行。

    可以发现,若biaib_i \leq a_i,那么对于所有j<ij<ijj不会受之前的影响。

    因此,可以把biaib_i \leq a_i作为分界点,进行DP。

    dpidp_i表示使得biaib_i \leq a_i的方案数目。只考虑大于等于ii的位。那么,dp0dp_0就是答案。

    枚举j>ij>i作为前一个分界点。那么,对于所有i<c<ji<c<j,都要求bc>acb_c>a_c

    再枚举所有的cc,那么bcb_c就可以很容易地用这个yy[c,j1][c,j-1]这些数位上的值来表示。

    于是,对于每个cc,可以得出一条不等式。把这些不等式联立,就能得到yy[i,j)[i,j)这些数位上的范围di,jd_{i,j}

    因此,dpidpi+di,j×dpjdp_i\leftarrow dp_i+d_{i,j}\times dp_j

    这样做的复杂度是O(k3q)O(k^3q)的,可以过。

    代码:

    #include <stdio.h>
    #include <vector>
    #include "biscuits.h"
    using namespace std;
    #define ll long long
    ll dp[70];
    ll count_tastiness(ll x, vector<ll> sz) {
        int k = sz.size();
        for (int i = 0; i < 62 - k; i++) sz.push_back(0);
        k = 62;
        for (int i = k; i >= 0; i--) {
            if (i == k) {
                dp[i] = 1;
                continue;
            }
            dp[i] = 0;
            for (int j = i + 1; j <= k; j++) {
                ll zx = 0, zd = (1ll << (j - i)) - 1, h = 0;
                for (int a = j - 1; a >= i; a--) {
                    h = h * 2 + sz[a];
                    ll z = (h / x + 1) << (a - i);
                    if (a > i) {
                        if (z > zx)
                            zx = z;
                    } else {
                        if (z - 1 < zd)
                            zd = z - 1;
                    }
                }
                if (zx <= zd)
                    dp[i] += dp[j] * (zd - zx + 1);
            }
        }
        return dp[0];
    }
    

    不难发现,求di,jd_{i,j}的过程可以优化。提前预处理出di,jd_{i,j},就可以O(k2)O(k^2)了。

    #include <stdio.h>
    #include <vector>
    #include "biscuits.h"
    using namespace std;
    #define ll long long
    ll dp[70], zz[70][70], dd[70][70];
    ll count_tastiness(ll x, vector<ll> sz) {
        int k = sz.size();
        for (int i = 0; i < 62 - k; i++) sz.push_back(0);
        for (int j = 1; j <= 62; j++) {
            ll zx = 0, h = 0;
            for (int a = j - 1; a >= 0; a--) {
                zz[a][j] = zx;
                h = h * 2 + sz[a];
                ll z = (h / x + 1) << a;
                if (z > zx)
                    zx = z;
                dd[a][j] = z;
            }
        }
        for (int i = 62; i >= 0; i--) {
            if (i == 62) {
                dp[i] = 1;
                continue;
            }
            dp[i] = 0;
            for (int j = i + 1; j <= 62; j++) {
                ll zx = (zz[i][j] >> i), zd = (1ll << (j - i));
                if ((dd[i][j] >> i) < zd)
                    zd = (dd[i][j] >> i);
                if (zx <= zd)
                    dp[i] += dp[j] * (zd - zx);
            }
        }
        return dp[0];
    }
    
    
    • 1

    信息

    ID
    5999
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者