「Bzoj 1049」「HAOI2006」数字序列 (DP+LIS+结论)

Bzoj 1049
题意:现在我们有一个长度为$n$的整数序列$A$。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。

第一问即经典问题。考虑不严格上升的序列,则为$n-LIS$,(这里的$LIS$为不下降的)
严格上升,则说明$i-j \geq a_i-a_j$,不然这个区间不可能严格上升。

移项,则$i-a_i \geq j-a_j$,那么将原来的$a_i$变为$a_i-i$按照同样方法处理即可。

对于第二问,我们设$f(i)$为$[1,i]$的答案,则
$$
f(i)=\max\limits_{dp(j)+1=dp(i)}(f(j)+w(j+1,i))
$$
那么我们怎么计算$w$呢
我们可以发现对于任意区间$[i,j]$都$\exists k$使得$[i,k]=a_i$, $[k+1,j]=a_j$,并且一定$\exists k$使得其为最优策略
那么对于每个$w$暴力$k$的位置。由于数据随机,所以$i$的的决策点$j$很少,复杂度跑不满

注意代码细节,加上前后最小值最大值方便操作

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db long double
#define fir first
#define sec second
#define mp make_pair
using namespace std;

namespace flyinthesky {

    const LL MAXN = 35000 + 10, INF = 1e10;

    LL n, a[MAXN], b[MAXN], len;
    LL dp[MAXN], f[MAXN], cst1[MAXN], cst2[MAXN];
    vector<LL > vec[MAXN];

    LL abss(LL x) {return x > 0 ? x : -x;}

    void clean() {
        len = 0;
    }
    int solve() {

        clean();
        cin >> n;
        for (LL i = 1; i <= n; ++i) scanf("%lld", &a[i]), a[i] -= i;
        ++n, a[n] = INF;

        b[++len] = a[1], dp[1] = 1;
        for (LL i = 2; i <= n; ++i) {
            if (a[i] >= b[len]) b[++len] = a[i], dp[i] = len;
            else {
                int pos = upper_bound(b + 1, b + 1 + len, a[i]) - b; // 1 upper_bound
                b[pos] = a[i], dp[i] = pos;
            }
        }
        printf("%lld\n", n - len);

        a[0] = -INF;
        vec[0].push_back(0);
        for (LL i = 1; i <= n; ++i) {
            f[i] = INF;
            for (LL j = 0; j < (LL)vec[dp[i] - 1].size(); ++j) {
                LL v = vec[dp[i] - 1][j];
                if (a[v] > a[i]) continue ;
                cst1[v - 1] = cst2[v - 1] = 0;
                for (LL k = v; k <= i; ++k) cst1[k] = abss(a[k] - a[v]), cst2[k] = abss(a[k] - a[i]);
                for (LL k = v + 1; k <= i; ++k) cst1[k] += cst1[k - 1], cst2[k] += cst2[k - 1];
                for (LL k = v; k <= i; ++k) {
                    LL now = cst1[k] - cst1[v] + cst2[i] - cst2[k]; 
                    f[i] = min(f[i], f[v] + now);
                }
            }
            vec[dp[i]].push_back(i);
        }

        printf("%lld\n", f[n]);

        return 0;
    }
}
int main() {
    flyinthesky::solve();
    return 0;
}
/*
5
1 2 4 3 1
*/
------ 本文结束 ------