📌  相关文章
📜  通过用所有先前元素的GCD最接近的幂替换每个元素来修改数组(1)

📅  最后修改于: 2023-12-03 15:42:03.154000             🧑  作者: Mango

通过用所有先前元素的GCD最接近的幂替换每个元素来修改数组

在一些算法竞赛中,需要用到一些特殊的操作来对数组进行修改。这里介绍一种比较常见的操作——通过用所有先前元素的GCD最接近的幂替换每个元素来修改数组。

操作介绍

给定一个长度为 $n$ 的数组 $a$,我们需要用满足下面两个条件的数 $x$ 替换每个元素:

  1. $x$ 是 $2$ 的正整数幂,即 $x = 2^k$。
  2. 对于 $i\in[2, n]$,满足 $x$ 是 $\gcd({a_1, a_2, \cdots, a_{i-1}})$ 的最接近整数幂。

换句话说,我们要把每个元素替换成一个离前面元素的 $\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$ 为元素的最大值。显然这个做法太慢了,在实际代码实现中需要使用一些优化技巧。