给定一个长度为 的序列 。若 ,则可以删除 ,后面的数依次向前填充,求最少剩下多少数。
先差分。令 ,则 等同于 ,删除也就是把 和 合并,即翻倍。
只能不断翻倍,故若 ,则 和 一定不能合并( 特殊讨论)。可以按照 把 数组划分。
定义 ,定义 表示前 个最多合并个数, 表示以 为右端点,合并出 的左端点,用类似倍增的方法转移 ,通过 转移 。
constexpr unsigned long long lowbit(unsigned long long x) { return x & -x; }
constexpr unsigned long long getexp(unsigned long long x) { return std::popcount(lowbit(x) - 1); }
void solve()
{
int n;
scanf("%d", &n);
std::vector<int> a(n);
for (auto &i : a) scanf("%d", &i);
std::vector<long long> da(n);
for (int i = 1; i < n; i++) da[i] = (long long)a[i] - a[i - 1];
int ans = n;
{
int i = 1;
while (i < n) {
int j = i + 1;
if (da[i] == 0) {
while (j < n && da[j] == 0) j++;
ans -= j - i - 1;
} else {
auto pre = da[i] / (signed)lowbit(std::abs(da[i]));
while (j < n && da[j] != 0 && da[j] / (signed)lowbit(std::abs(da[j])) == pre) j++;
std::vector<int> f(j - i);
std::vector<std::vector<int>> g(j - i, std::vector<int>(32, -1));
for (int p = 0; p < j - i; p++) {
int mink = getexp(std::abs(da[p + i]));
g[p][mink] = p;
if (p > 0) f[p] = f[p - 1];
for (int k = mink + 1; k < 32; k++) {
if (g[p][k - 1] - 1 >= 0 && g[g[p][k - 1] - 1][k - 1] >= 0) {
g[p][k] = g[g[p][k - 1] - 1][k - 1];
if (g[p][k] > 0) f[p] = std::max(f[p], f[g[p][k] - 1] + (p - g[p][k]));
else f[p] = std::max(f[p], p - g[p][k]);
}
}
}
ans -= f.back();
}
i = j;
}
}
printf("%d\n", ans);
}
代码时注意处理负数、0 的情况。