【BZOJ1049】[HAOI2006]数字序列
【BZOJ1049】[HAOI2006]数字序列
题面
题解
第一问
直接做不是很好做,可以转化为最大化不修改的点最多
对于原数列,对于\(i,j\)(\(i<j\)),使得\(i,j\)不被修改的必要条件是\(A_j-A_i\geq j-i\)
于是可以写出一个\(dp\)方程:\(dp_i=max_{j=0}^{i-1}\;dp_j+1(A_j-A_i\geq j-i)\)
将不等式\(A_j-A_i\geq j-i\)变为\(A_j-j\geq A_i-i\),可以看出第一问就是求数列\(B_i=A_i-i\)的最长不降子序列的长度,再用\(n\)减去即可
第二问
要求你在第一问的情况下要求变化幅度最小。
在第一问的情况下对于每个有\(dp_j=dp_i+1\)的\(i,j\)(\(i<j\)),肯定是变化\(i,j\)中间的数
怎么变?
有一个结论:
对于\(i,j\)中间的位置\(k\),必有\(i\)$k$全变为$A_i$,$k+1$\(j\)全变为\(A_j\)
因为很(bu)显(hui)然(zheng)就不证了。。。
那么我们设\(g_i\)表示排好\(1\)~\(i\)的最小代价
则\(g_j=min_{i=0}^{j-1}g_i+w(i,j)(dp_j=dp_i+1)\)(\(w(i,j)\)表示修改区间\([i,j]\)的贡献)
复杂度\(O(能过)\)
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define int long long
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int MAX_N = 35005;
const int INF = 1e13;
int N, a[MAX_N], b[MAX_N], c[MAX_N];
int f[MAX_N], g[MAX_N], s1[MAX_N], s2[MAX_N];
vector<int> vec[MAX_N];
signed main () {
N = gi(); for (int i = 1; i <= N; i++) a[i] = gi(), b[i] = a[i] - i;
fill(&f[1], &f[N + 1], INF);
for (int i = 1; i <= N; i++) c[i] = upper_bound(&f[1], &f[N + 1], b[i]) - f , f[c[i]] = b[i];
printf("%lld\n", N - (long long)(lower_bound(&f[1], &f[N + 1], INF) - f - 1));
N++; c[N] = lower_bound(&f[1], &f[N + 1], INF) - f; b[N] = INF;
fill(&g[1], &g[N + 1], INF);
b[0] = -INF;
for (int i = 0; i <= N; i++) vec[c[i]].push_back(i);
for (int i = 1; i <= N; i++) {
vector<int> :: iterator ite; int prv = c[i] - 1;
for (ite = vec[prv].begin(); ite != vec[prv].end(); ++ite) {
int k = *ite;
if (k > i) break; if (b[k] > b[i]) continue;
s1[k - 1] = s2[k - 1] = 0;
for (int j = k; j <= i; j++) s1[j] = s1[j - 1] + abs(b[k] - b[j]);
for (int j = k; j <= i; j++) s2[j] = s2[j - 1] + abs(b[i] - b[j]);
for (int j = k; j <= i; j++) g[i] = min(g[i], g[k] + s1[j] - s1[k - 1] + s2[i] - s2[j]);
}
}
printf("%lld\n", g[N]);
return 0;
}