Arithmetic Operations
[Link](Problem - E - Codeforces)
题意
给你一个数组 a a a,每次操作可以选择一个 a i a_i ai将其变成任意一个数 x x x,问你最少操作多少次使之变成一个等差数列。
思路
不好判断修改哪几个数,但是哪些数不变比较好判断。我们可以枚举公差 d d d,首先只考虑 d > = 0 d>=0 d>=0的情况对于 d < 0 d<0 d<0的情况,将原数组反转即可,对于每个 d d d我们让 a i − ( i − 1 ) ∗ d a_i-(i-1)*d ai−(i−1)∗d,相当于把偏移量减去,之后剩下的最多的就是我们要留下数,剩下的都是需要改变的。设 m = m a x ( a i ) m=max(a_i) m=max(ai),我们的公差不会超过这个数,例如 a 1 , a 2 , a 3 a_1,a_2,a_3 a1,a2,a3三个数 m = a 2 m=a_2 m=a2,如果 d < = m d<=m d<=m则我们最多只需要改变 a 1 , a 3 a_1,a_3 a1,a3,若 d > m d>m d>m则必须修改 a 1 , a 2 , a 3 a_1,a_2,a_3 a1,a2,a3。
但是这样枚举复杂度为 O ( n m ) O(nm) O(nm)太高了,考虑分类对于 d < m d<\sqrt m d<m的我们枚举 d d d,复杂度为 O ( n m ) O(n\sqrt m) O(nm)。
对于 d ≥ m d\ge \sqrt m d≥m情况,首先我们来证任意一个 a i a_i ai最多可能和 a i + m a_{i+\sqrt m} ai+m一起保留,假设 j > i + m j>i+\sqrt m j>i+m,则 a j − a i ≥ d × m > m a_j-a_i\ge d\times \sqrt m >m aj−ai≥d×m>m,由于 m a x ( a i ) = = m max(a_i)==m max(ai)==m因此显然不对,所以 i i i和 j > i + m j>i+\sqrt m j>i+m的数只能保留一个。由于要让一些数有相同的 d d d,因此将 i → j ( j ≤ i + m ) i\to j(j\le i+\sqrt m) i→j(j≤i+m)连一条边,边权为 a j − a i j − i = d \frac{a_j-a_i}{j -i}=d j−iaj−ai=d(如果可以整除),最多会有 n m n\sqrt m nm条边,我们相当于要找最多的具有相同边权的边,由于是个拓扑图,可以直接用 f [ i ] [ j ] : 以 i 结 尾 的 为 d 的 边 有 多 长 f[i][j]:以i结尾的为d的边有多长 f[i][j]:以i结尾的为d的边有多长,转移即可,复杂度为 O ( n m ) O(n\sqrt m) O(nm)。
由于第二部分的图比较稀疏,因此实际上以小于 m \sqrt m m来分类更优一些。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
int S = 200;
int a[N];
int b[N * 200];
int solve() {
int res = 0;
for (int d = 0; d < S; d ++) {
for (int i = 0; i < n; i ++) {
b[a[i] + d * (n - i)] ++;
res = max(res, b[a[i] + d * (n - i)]);
}
for (int i = 0; i < n; i ++)
b[a[i] + d * (n - i)] --;
}
map<int, int> f[n + 1];
for (int i = 0; i < n; i ++)
for (int j = max(0, i - N / S); j < i; j ++) {
int d = (a[i] - a[j]) / (j - i);
int r = (a[i] - a[j]) % (j - i);
if (d >= S && !r) {
f[i][d] = max(f[i][d], f[j][d] + 1);
res = max(res, f[i][d] + 1);
}
}
return res;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (int i = 0; i < n; i ++) cin >> a[i];
int ans = solve();
reverse(a, a + n);
ans = max(ans, solve());
cout << n - ans << '\n';
return 0;
}