📅  最后修改于: 2023-12-03 15:42:03.154000             🧑  作者: Mango
在一些算法竞赛中,需要用到一些特殊的操作来对数组进行修改。这里介绍一种比较常见的操作——通过用所有先前元素的GCD最接近的幂替换每个元素来修改数组。
给定一个长度为 $n$ 的数组 $a$,我们需要用满足下面两个条件的数 $x$ 替换每个元素:
换句话说,我们要把每个元素替换成一个离前面元素的 $\gcd$ 最近的 $2$ 的幂。
我们首先可以想到,对于后面的任意一个位置 $i$,要满足 $x$ 是 $\gcd({a_1, a_2, \cdots, a_{i-1}})$ 的最接近整数幂,那么 $x$ 和 $\gcd({a_1, a_2, \cdots, a_{i-1}})$ 必然是同奇偶性的。也就是说,如果我们能够保证每次替换后 $\gcd({a_1, a_2, \cdots, a_i})$ 始终为偶数,那么替换成偶数幂的数一定是最优的。
接下来我们考虑如何实现这个做法。我们可以先用 $p_0 = 1$ 表示 $2^0$,然后每次枚举 $j \in [0, 31]$,计算 $a_i$ 与 $2^j p_k$($k \in [0, i-1]$)的 $\gcd$,找到与 $a_i$ 的距离最近的 $j$,将 $p_k$ 更新为 $2^j$。注意我们需要将 $2^0$ 去掉,因为它永远不会比 $2^1$ 更优。
下面是 C++ 的实现代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, a[N], p[N], ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
set<int> S;
S.insert(1);
for (int j = 2; j <= 32; ++j) {
for (int k = 1; k < i; ++k) {
int t = __gcd(a[k], 1 << j);
if (t == (1 << j)) {
S.clear();
break;
}
if (t == (1 << (j - 1))) {
S.insert(1 << (j - 1));
break;
}
if (t != 1) S.insert(t);
}
}
for (int x : S) {
if (abs(x - a[i]) < abs(p[i] - a[i]))
p[i] = x;
}
}
for (int i = 1; i <= n; ++i) {
ans += __lg(p[i]);
printf("%d ", p[i]);
}
printf("\n%d\n", ans);
return 0;
}
代码的时间复杂度为 $O(n^3\log(C))$,其中 $C$ 为元素的最大值。显然这个做法太慢了,在实际代码实现中需要使用一些优化技巧。