目录

P2501-[HAOI2006]数字序列

P2501-[HAOI2006]数字序列

题目:

题目描述:

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

输入格式:

第一行是一个整数,表示序列长度 $n$。
第二行有 $n$ 个整数,第 $i$ 个整数表示序列的第 $i$ 项 $a_i$。

输出格式:

第一行输出一个整数,表示最少需要改变多少个数。

第二行输出一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

样例:

样例输入 1:

1
2
4
5 2 3 5

样例输出 1:

1
2
1
4

思路:

实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "ybwhead/ios.h"
// #define rep(i, m, n) for (int i = m; i <= n; ++i)
// #define dop(i, m, n) for (int i = m; i >= n; --i)
// #define each(i, m, v) for (int i = head[m], v = e[i].to; i; i = e[i].next, v = e[i].to)
// #define lowbit(x) (x & (-x))
// #define ll long long
const long long INF = 1073741824;
// #define re register
// using namespace std;
const int maxn = 35010;
int n, a[maxn], c[maxn], dp[maxn], len, tmp, num, head[maxn];
long long f[maxn], sum1[maxn], sum2[maxn];
struct Edge
{
    int to, nxt;
} e[maxn];
void Add(int from, int to)
{
    e[++num].to = to;
    e[num].nxt = head[from];
    head[from] = num;
} //邻接表
int main()
{
    yin >> n;
    for (int i = 1; i <= n; i++)
        yin >> a[i], a[i] = a[i] - i;
    for (int i = 1; i <= n; i++)
        c[i] = INF;
    c[0] = -INF;
    dp[1] = 1;
    len = 1;
    a[++n] = INF;
    c[1] = a[1];
    a[0] = -INF;
    for (int i = 2; i <= n; i++)
    {
        tmp = upper_bound(c, c + 1 + len, a[i]) - c;
        len = max(len, tmp);
        dp[i] = tmp;
        c[tmp] = min(c[tmp], a[i]);
    }
    yout << n - dp[n] << endl;
    for (int i = n; i >= 0; i--)
        Add(dp[i], i), f[i] = INF;
    f[0] = 0;
    for (int i = 1; i <= n; i++)
        for (int j = head[dp[i] - 1], to = e[j].to; j; j = e[j].nxt, to = e[j].to)
        {
            if (to > i)
                break;
            if (a[to] > a[i])
                continue;
            for (int k = to; k <= i; k++)
                sum1[k] = abs(a[k] - a[to]), sum2[k] = abs(a[k] - a[i]);
            for (int k = to + 1; k <= i; k++)
                sum1[k] += sum1[k - 1], sum2[k] += sum2[k - 1];
            for (int k = to; k <= i - 1; k++)
                f[i] = min(f[i], f[to] + sum1[k] - sum1[to] + sum2[i] - sum2[k]);
        }
    yout << f[n] << endl;
    return 0;
}