目录

P3648-[APIO2014]序列分割

P3648-[APIO2014]序列分割

题目:

题目描述:

你正在玩一个关于长度为 $n$ 的非负整数序列的游戏。这个游戏中你需要把序列分成 $k + 1$ 个非空的块。为了得到 $k + 1$ 块,你需要重复下面的操作 $k$ 次:

选择一个有超过一个元素的块(初始时你只有一块,即整个序列)

选择两个相邻元素把这个块从中间分开,得到两个非空的块。

每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。

输入格式:

第一行包含两个整数 $n$ 和 $k$。保证 $k + 1 \leq n$。

第二行包含 $n$ 个非负整数 $a_1, a_2, \cdots, a_n$ $(0 \leq a_i \leq 10^4)$,表示前文所述的序列。

输出格式:

第一行输出你能获得的最大总得分。

第二行输出 $k$ 个介于 $1$ 到 $n - 1$ 之间的整数,表示为了使得总得分最大,你每次操作中分开两个块的位置。第 $i$ 个整数 $s_i$ 表示第 $i$ 次操作将在 $s_i$ 和 $s_{i + 1}$ 之间把块分开。

如果有多种方案使得总得分最大,输出任意一种方案即可。

样例:

样例输入 1:

1
2
7 3
4 1 3 4 0 2 3

样例输出 1:

1
2
108
1 3 5

思路:

实现:

 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
#include "ybwhead/ios.h"
const int maxn = 5e5 + 10;
long long a[maxn], sum[maxn];
long long g[maxn], f[maxn];
#define Y(x) (g[x] - sum[x] * sum[x])
#define X(x) (-sum[x])
double slope(int x, int y)
{
    if (sum[x] == sum[y])
        return -1e18;
    return (double)(Y(y) - Y(x)) / (X(y) - X(x));
}
int nxt[maxn][200];
int n, k;
int q[maxn];
int main()
{
    yin >> n >> k;
    for (int i = 1; i <= n; i++)
        yin >> a[i], sum[i] = a[i] + sum[i - 1];
    int l, r;
    for (int xx = 1; xx <= k; xx++)
    {
        for (int i = 1; i <= n; i++)
            g[i] = f[i];
        l = 1;
        r = 0;
        for (int i = 1; i <= n; i++)
        {
            while (l < r && slope(q[l], q[l + 1]) <= sum[i])
                ++l;
            f[i] = 0;
            if (l <= r)
            {
                int tmp = q[l];
                nxt[i][xx] = tmp;
                f[i] = g[tmp] + sum[tmp] * (sum[i] - sum[tmp]);
                // yout << g[tmp] << endl;
            }
            while (l < r && slope(q[r - 1], q[r]) >= slope(q[r], i))
                --r;
            q[++r] = i;
        }
    }
    yout << f[n] << endl;
    int tmp = n, kk = k;
    while (kk)
    {
        tmp = nxt[tmp][kk];
        yout << tmp << ' ';
        kk--;
    }
}