1 条题解
-
0
自动搬运
来自洛谷,原作者为

lnzwz
**搬运于
2025-08-24 22:24:28,当前版本为作者最后更新于2020-10-06 20:34:33,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题我的方法比较奇怪。
题意:
有种物品,第个物品有个,权值为。
求有多少个,使得可以选出组物品,每组的和都为。
先考虑如何判定一个是否可行:
从最高位开始,依次求出第i位需要的数目。若的第位为1,则。
如果,那么说明够用,进入下一位。
如果,则选上所有后,还剩个。那么这些只能用两倍的来凑。因此把加上。
这样,只要,则说明这个可行。
可以发现,若,那么对于所有,不会受之前的影响。
因此,可以把作为分界点,进行DP。
设表示使得的方案数目。只考虑大于等于的位。那么,就是答案。
枚举作为前一个分界点。那么,对于所有,都要求。
再枚举所有的,那么就可以很容易地用这个在这些数位上的值来表示。
于是,对于每个,可以得出一条不等式。把这些不等式联立,就能得到在这些数位上的范围。
因此,。
这样做的复杂度是的,可以过。
代码:
#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]; }不难发现,求的过程可以优化。提前预处理出,就可以了。
#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
- 上传者