1 条题解
-
0
说句闲话:
- 出题人这题做了半年。
- 当 时,贪心上界是 (,)。
Part 0
显然,无解是不可能的,因为 个 的和就是 ,而 ,因此这一定是合法的方案。
Part 1
设答案为 ,求证:,其中 。
证明:
-
先证明 :
若 ,则 。
显然 ,因此 。
于是,,即 。
由于每个完全平方数都不大于 ,因此 个完全平方数的和最大只能是 。
然而,,因此, 个完全平方数的和绝不可能等于 ,矛盾。
综上, 的假设不成立,因此 ,得证。
-
再证明 :
设 。
显然 ,因此 ,于是 。
根据这个定理,总有四个非负整数 ,使得 。
由于 ,而 均非负,因此 均不大于 ,满足「不大于 」的要求。
将 代入,得 。
此时,等式右边共 个完全平方数,故 (为 的项可以删去,因此可能少于 个),得证。
综上所述,,原命题得证。
显然,由于 , 的取值只有 种(,,,,)。因此,考虑暴力枚举 的值,每次判断是否可行(若 则直接跳过,保证 )。
Part 2
先来考虑一种错误的解法( 分):按照与加强前的原题中 解法类似的思路,使用完全背包求解,时间复杂度 ,超时。
显然,使得上述算法超时的主要原因是状态太多(对应 中的 )。要想优化此算法,必须减少状态数。
我们先来看一个例子:。显然,,答案是 。
不难发现,几乎所有的完全平方数都是 。 由于正向求解遇到了困难,考虑反向求解:每次枚举 的值时,先假设所有的完全平方数都是 ,再把一部分 修改为其它小于 的完全平方数,使总和减小(倒扣),反向求解。
于是,完全背包的定义发生了转变。
Part 3
定义 为:恰好倒扣 至少需要修改的 的个数,若无法恰好倒扣 则其值为 。()
初始状态:
- (解释:前者无需倒扣,后者假定无法恰好倒扣,等待更新。)
状态转移方程(顺序:按 升序):
- 其中 ,保证
- (解释:修改 个 (改为 ),倒扣 。)
由于需要从 倒扣至 ,共倒扣了 ,因此 即为所需的答案。
注意:若 ,则表明没有足够的 用于倒扣时的修改(或根本不可能恰好倒扣,此时 ),该 的值必须舍去。
Part 4
求证:倒扣的总量(,即当 时下标 的上界)不大于 。
证明:
前面已经证明 。
-
当 时:
此时有 ,因此 ,即 。
又因为前面已经证明 ,因此 。
于是 $k \cdot m^2 \le (p+4) \cdot m^2 \le n+4 \cdot m^2-1$,即 。
由此可得 。
-
当 时:
显然此时 。该等式右边共 个完全平方数,故 (前面已经证明 )。
将上面两个等式代入计算,得 。显然 。
综上所述,,原命题得证。
若 ,求证:。
证明:
根据上文,模拟可得,过程略去。原命题得证。Part 5
最终,(每组数据)时间复杂度 ,空间复杂度 ,可以通过此题。
Part 6
std:
#include <bits/stdc++.h> using namespace std; const int M = 500 + 12; const int K = 4 * M * M + 12; const int W = 58; char dp[K]; signed main() { int T; scanf("%d", &T); while (T--) { int m, Q; scanf("%d %d", &m, &Q); memset(dp, 0x7e, sizeof dp); dp[0] = 0; for (int u = 0; u <= 4 * m * m; u++) for (int i = m - 1; i >= 1; i--) { int x = m * m - i * i; if (u + x > 4 * m * m) break; if (dp[u + x] > dp[u] + 1) dp[u + x] = dp[u] + 1; } while (Q--) { long long n; scanf("%lld", &n); long long k = n / (m * m); while (k * m * m < n || dp[k * m * m - n] >= W || dp[k * m * m - n] > k) k++; printf("%lld\n", k); } } return 0; }
- 1
信息
- ID
- 240
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 7
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者