P8447 (Official)
说句闲话:
- 出题人这题做了半年。
- 当
m \le 500 时,贪心上界是13853793 (n=13853793=57 \times 493^2 ,m=494 )。
Part 0
显然,无解是不可能的,因为
Part 1
设答案为
k ,求证:p \le k \le p+4 ,其中p=\lfloor\dfrac{n}{m^2}\rfloor 。
证明:
-
先证明
k \ge p :若
k<p ,则k \cdot m^2 < p \cdot m^2 。显然
p \le \dfrac{n}{m^2} ,因此p \cdot m^2 \le n 。于是,
k \cdot m^2 < p \cdot m^2 \le n ,即k \cdot m^2 < n 。由于每个完全平方数都不大于
m^2 ,因此k 个完全平方数的和最大只能是k \cdot m^2 。然而,
k \cdot m^2 < n ,因此,k 个完全平方数的和绝不可能等于n ,矛盾。综上,
k<p 的假设不成立,因此k \ge p ,得证。 -
再证明
k \le p+4 :设
n=p \cdot m^2 +q 。显然
p \le \dfrac{n}{m^2}<p+1 ,因此p \cdot m^2 \le n<(p+1) \cdot m^2 ,于是0 \le q <m^2 。根据这个定理,总有四个非负整数
e_1,e_2,e_3,e_4 ,使得q=(e_1)^2+(e_2)^2+(e_3)^2+(e_4)^2 。由于
q <m^2 ,而(e_1)^2,(e_2)^2,(e_3)^2,(e_4)^2 均非负,因此(e_1)^2,(e_2)^2,(e_3)^2,(e_4)^2 均不大于m^2 ,满足「不大于m^2 」的要求。将
q 代入,得n=p \cdot m^2 +(e_1)^2+(e_2)^2+(e_3)^2+(e_4)^2 。此时,等式右边共
(p+4) 个完全平方数,故k \le p+4 (为0 的项可以删去,因此可能少于(p+4) 个),得证。
综上所述,
显然,由于
Part 2
先来考虑一种错误的解法(
显然,使得上述算法超时的主要原因是状态太多(对应
我们先来看一个例子:
不难发现,几乎所有的完全平方数都是
于是,完全背包的定义发生了转变。
Part 3
定义
初始状态:
-
w_{0}=0,w_{j}=+\infty. -
j \neq 0. - (解释:前者无需倒扣,后者假定无法恰好倒扣,等待更新。)
状态转移方程(顺序:按
-
w_{j} = \min(w_{j},w_{j-(m^2-x^2)}+1). - 其中
x=1,2,\ldots,m-1 ,保证j-(m^2-x^2) \ge 0. - (解释:修改
1 个m^2 (改为x^2 ),倒扣m^2-x^2 。)
由于需要从
注意:若
Part 4
求证:倒扣的总量(
k \cdot m^2 - n ,即当i=m 时下标j 的上界)不大于4 \cdot m^2-1 。
证明:
前面已经证明
-
当
p \cdot m^2 < n 时:此时有
p \cdot m^2 \le n-1 ,因此p \cdot m^2 +4 \cdot m^2 \le n+4 \cdot m^2-1 ,即(p+4) \cdot m^2 \le n+4 \cdot m^2-1 。又因为前面已经证明
k \le p+4 ,因此k \cdot m^2 \le (p+4) \cdot m^2 。于是
k \cdot m^2 \le (p+4) \cdot m^2 \le n+4 \cdot m^2-1 ,即k \cdot m^2 \le n+4 \cdot m^2-1 。由此可得
k \cdot m^2-n \le 4 \cdot m^2-1 。 -
当
p \cdot m^2 = n 时:显然此时
n = p \cdot m^2 。该等式右边共p 个完全平方数,故k =p (前面已经证明k \ge p )。将上面两个等式代入计算,得
k \cdot m^2 - n =0 。显然4 \cdot m^2-1>0 。
综上所述,
若
w_{j} \neq +\infty ,求证:w_{j} \le 57 。
证明:
根据上文,模拟可得,过程略去。原命题得证。
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;
}