Codeforces Round #778 (Div. 1 + Div. 2)

E. Arithmetic Operations

题目link

题目描述

给定一个长度为 \(n (n \le 10 ^ 5)\) 数列 \(a (a_i \le 10^5)\) ,用最少的修改次数使其变为一个等差数列。


SOLUTION

考虑根号分治,对于公差 $ |d| \le 200$ 的,我们可以暴力 \(O(n)\) 判断整个区间,对于公差 \(|d| > 200\) 的,整个等差数列区间长度一定是小于等于 \(500\) 的,因此我们可以枚举所有的长度为 \(500\) 的区间,暴力判断即可。

\(Trick\) : 判断一段区间最多有多少项符合公差为 \(d\),可以将 a[i] -= i * d,然后判断最多有多少个数字相同即可。

CODE

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

#define rep(i, a, b) for (int i(a); i <= b; ++ i ) 
#define dec(i, a, b) for (int i(b); i >= a; -- i ) 

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 1E5 + 10;

int a[N], b[N];

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);


  int n; cin >> n;
  for (int i = 1; i <= n; i ++ ) cin >> a[i];

  int ans = 0;

  auto get = [&] (int m) {
    int ret = 0; sort(b + 1, b + 1 + m);
    for (int i = 1, j = 1; i <= m; i ++ ) {
      while (j + 1 <= m && b[j + 1] == b[i]) {
        ++ j;
      }
      chkmax(ret, j - i + 1);
      i = j;
    }
    return ret;
  };
  // -200 <= d <= 200
  for (int i = -200; i <= 200; i ++ ) {
    for (int j = 1; j <= n; j ++ ) {
      b[j] = a[j] - i * j;
    }
    chkmax(ans, get(n));
  }
  //等差数列限定 [i, i + 499] 这一段,因此只需要考虑 (a_j - a_i) / (j - i) 即是公差
  for (int i = 1; i < n; i ++ ) {
    int m = 0, px = min(n, i + 499);
    for (int j = i + 1; j <= px; j ++ ) {
      if ( (a[j] - a[i]) % (j - i) == 0) {
        b[++ m] = (a[j] - a[i]) / (j - i);
      }
    }
    chkmax(ans, get(m) + 1);
  }

  cout << n - ans << "\n";

  return 0;
}
posted @ 2022-09-08 14:47  ccz9729  阅读(34)  评论(0编辑  收藏  举报