Codeforces Round #778 (Div. 1 + Div. 2)
E. Arithmetic Operations
题目描述
给定一个长度为 \(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;
}