CF623B Array GCD

CF623B Array GCD

给定一个序列 (a_i) ,可以进行一下两种操作:

  • 删除一段长度为 (m) 的区间 ((m<n)) ,花费为 (m imes A) ,只能操作一次
  • 可以花费 (b) 让一个元素 (+1)(-1) ,每个元素只能操作一次

求使得剩下的数最大公约数大于 (1) 的最小花费

(nleq10^6, a_{i}in[2, 10^9])

贪心,动态规划


由于删除的区间长度小于 (n) ,则最优解剩下的序列一定包含 (a_1)(a_n) ,则最后的 (gcd) 一定是 (a_1, a_1+1, a_1-1, a_n, a_n+1, a_n-1) 其中之一的约数

由于每个数的不同质因子个数是 (log) 级别的,可以考虑枚举这 (6) 个数各自的质因子当作最大公约数

当确定了 (gcd) ,考虑如何求出最小花费

可以设计一个 dp,令 (f_{i, 0/1/2}) 为截至第 (i) 位,没有删除过区间 / 正在删除一段区间 / 已经删除了一段区间 所能得到的最小花费

可以列出方程 (egin{cases}f_{i, 0}=f_{i-1, 0}+cost_i\f_{i, 1}=min(f_{i-1, 0}, f_{i-1, 1})+A\f_{i, 2}=min(f_{i-1, 1}, f_{i-1, 2})+cost_iend{cases})

时间复杂度 (O(nlog n))

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e6 + 10;
ll f[3][maxn];
int n, A, B, a[maxn];

inline ll calc(int x, int P) {
  if (x % P == 0) return 0;
  if ((x + 1) % P == 0 || (x - 1) % P == 0) {
    return B;
  }
  return 1ll << 40;
}

inline ll check(int P) {
  bool flg = 1;
  f[1][0] = f[2][0] = 1ll << 40;
  for (int i = 1; i <= n; i++) {
    ll v = calc(a[i], P);
    f[0][i] = f[0][i - 1] + v;
    f[1][i] = min(f[0][i - 1], f[1][i - 1]) + A;
    f[2][i] = min(f[1][i - 1], f[2][i - 1]) + v;
    flg &= i == 1 || f[0][i - 1] > f[1][i - 1];
  }
  return min(f[0][n], min(flg ? 1ll << 60: f[1][n], f[2][n]));
}

inline ll factor(int x) {
  int tmp = sqrt(x);
  ll res = 1ll << 60;
  for (int i = 2; i <= tmp; i++) {
    if (x % i == 0) {
      res = min(res, check(i));
      while (x % i == 0) x /= i;
    }
  }
  if (x > 1) res = min(res, check(x));
  return res;
}

int main() {
  scanf("%d %d %d", &n, &A, &B);
  for (int i = 1; i <= n; i++) {
    scanf("%d", a + i);
  }
  if (n == 1) return puts("0");
  int s[7];
  s[1] = a[1];
  s[2] = a[1] - 1;
  s[3] = a[1] + 1;
  s[4] = a[n];
  s[5] = a[n] - 1;
  s[6] = a[n] + 1;
  ll ans = 1ll << 60;
  for (int i = 1; i < 7; i++) {
    ans = min(ans, factor(s[i]));
  }
  printf("%I64d", ans);
  return 0;
}
原文地址:https://www.cnblogs.com/Juanzhang/p/11001692.html