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;
}
posted @ 2024-10-14 16:52  quanjun  阅读(2)  评论(0编辑  收藏  举报