CF1718D. Many Perfect Squares 题解 枚举
题目链接:https://codeforces.com/problemset/problem/1781/D
题目大意
给你一个长度为 \(n\) 的数列 \(a_1, a_2, \ldots, a_n\)。
你需要在 \([0, 10^{18}]\) 范围内找到一个整数 \(x\),并将数列 \(a\) 中每个元素的数值都加上 \(x\) 得到一个新的数列 \(a_1 + x, a_2 + x, \ldots, a_n + x\)。
你希望使新的数列中平方数的个数尽可能地多。
求:你能够得到的新数列中最多的平方数的个数。
题解
首先答案至少是 \(1\) 。
接着我们考虑能否得到至少 \(2\) 个数? 我们可以枚举 \(i\) 和 \(j\)(\(1 \le i \lt j \le n\)),并找出 \(x\) 的哪些值 \(a_i + x\) 和 \(a_j + x\) 都是完全平方数。
我们可以写出两个等式: \(a_i + x = p^2\) 和 \(a_j + x = q^2\) ,对于某个非负整数 \(p \lt q\) 。
我们将第一个等式减去第二个等式,并应用两个平方差的公式: \(a_j - a_i = q^2 - p^2 = (q - p)(q + p)\) 。可知, \(q - p\) 是 \(a_j - a_i\) 的正的因数。
我们可以以 \(O(\sqrt{a_j - a_i})\) 的时间复杂度枚举 \(a_j - a_i\) 的所有因数。
对于每个这样的因数 \(d\) ,我们都可以得到一个简单的方程组,即 \(p\) 和 \(q\) 的方程组,如下:
\(\begin{cases} q - p = d \\ q + p = \frac{a_j - a_i}{d} \end{cases}\)
\(\Rightarrow\)
\(\begin{cases} p = \frac{1}{2}(\frac{a_j - a_i}{d} - d) \\ q = \frac{1}{2}(\frac{a_j - a_i}{d} + d) \\ \end{cases}\)
如果 \(p\) 和 \(q\) 都是整数,那就意味着我们找到了 \(x\) 的候选值: \(x = p^2 - a_i = q^2 - a_j\) .
对于 \(x\) 的每个候选值,我们只需计算它的平方和并找出最大值即可。
示例程序:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T, n, a[55];
// 判断整数 a 是不是平方数
bool check(int a) {
int b = sqrt(a);
return b * b == a;
}
int cal() {
int res = 1; // 至少能得到一个平方数
for (int i = 1; i < n; i++) {
for (int j = i+1; j <= n; j++) {
int num = a[j] - a[i];
for (int d = 1; d * d <= num; d++) {
if (num % d || num / d % 2 != d % 2)
continue;
int p = (num / d - d) / 2;
int q = (num / d + d) / 2;
assert(p * p - a[i] == q * q - a[j]);
int x = p * p - a[i];
if (x < 0) // x 在 [0, 1e18] 范围内
continue;
int cnt = 0;
for (int k = 1; k <= n; k++)
if (check(a[k] + x))
cnt++;
// cout << "x = " << x << ", cnt = " << cnt << endl;
res = max(res, cnt);
}
}
}
return res;
}
signed main() {
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
cout << cal() << endl;
}
return 0;
}