【BZOJ1049】[HAOI2006]数字序列

【BZOJ1049】[HAOI2006]数字序列

题面

bzoj

洛谷

题解

第一问

直接做不是很好做,可以转化为最大化不修改的点最多

对于原数列,对于\(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; 
} 
posted @ 2018-12-26 14:37  heyujun  阅读(270)  评论(0编辑  收藏  举报