LG3648 [APIO2014] 序列分割

Problem

LG3648 [APIO2014] 序列分割

Analysis

正解思路

序列分割很容易想到两种思考:

  1. 反向转化为如何合并,区间 dp 解决。
  2. 证明分割顺序无影响,采用线性 dp 解决。

但显然这个方法一无法进行优化。那么考虑方法二,然后就比较容易证明出分割顺序确实无影响,因为对于任意一个块内的每个元素都与其他块内的所有元素相乘过。

然后就考虑 dp 状态如何设计:设 \(dp_{i,j}\) 为前 \(i\) 个元素划分了 \(j\) 次的最大价值。转移也比较显然:枚举前一个划分点 \(k\),有 \(dp_{i,j}\gets \max_{1\le k<i}\{dp_{k,j-1}+(sum_n-sum_i)(sum_i-sum_j)\}\)。此时复杂度为 \(O(n^2k)\)

仍然无法通过,状态已经很优了,所以考虑优化转移。

发现这个转移方程符合斜率优化的条件,那么就能优化到 \(O(nk)\)

\(k_1<k_2\),若 \(dp_{k_1}+(sum_n-sum_i)(sum_i-sum_{k_1})>dp_{k_2}+(sum_n-sum_i)(sum_i-sum_{k_2})\),则选择 \(k_1\),否则 \(k_2\)。继续转化不等式可得 \(\frac{dp_{k_2}-dp_{k_1}}{sum_{k_2}-sum_{k_1}}<sum_n-sum_i\)。(dp 第二维省略)

此时将 \((sum_k,dp_k)\) 看作平面上的一个点,那么就可以用单调队列维护。

错因总结

考虑了分割顺序是无影响的,但是没有想到其证明方法,且脑子中想的转移方程不清楚,如果写在纸上则可能可以写出朴素的转移方程,但是最后一步斜率优化可能还是想不到(已经遗忘了)。

AC Code

点击查看代码
#include <bits/stdc++.h>
#define ll long long 
#define ull unsigned long long 
#define i128 __int128
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define mk make_pair
#define INF 0x3f3f3f3f
#define INFx 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 1e5 + 10, M = 210;

int n, k;
ll a[N], s[N];
ll dp[N][M];
int from[N][M];
int q[N][M], hh[M], tt[M]; // 第 i 层的单调队列
int seq[M];

double slope(int j, int k1, int k2) {
    if (s[k1] == s[k2]) return 1e18;
    return (double)(dp[k2][j] - dp[k1][j]) / (s[k2] - s[k1]);
}

int main() {    
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    cin >> n >> k;

    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }

    for (int i = 1; i <= n; i ++) { 
        double lim = s[n] - s[i];
        for (int j = k; j >= 1; j --) {
            int &h1 = hh[j - 1], &t1 = tt[j - 1];
            while (h1 <= t1 - 1 && slope(j - 1, q[h1][j - 1], q[h1 + 1][j - 1]) >= lim) h1 ++;
            
            int t = q[h1][j - 1];
            dp[i][j] = dp[t][j - 1] + (ll)(s[n] - s[i]) * (s[i] - s[t]);
            from[i][j] = t;

            int &h2 = hh[j], &t2 = tt[j];
            while (h2 <= t2 - 1 && slope(j, q[t2][j], i) >= slope(j, q[t2 - 1][j], q[t2][j])) t2 --;
            q[++ t2][j] = i;
        }
    }

    ll ans = -1;
    int p = 0;
    for (int i = 1; i <= n; i ++) { // final cut
        if (dp[i][k] > ans) ans = dp[i][k], p = i;
    }
    for (int i = k; i >= 1; i --) {
        seq[i] = p;
        p = from[p][i];
    }

    cout << ans << '\n';
    for (int i = 1; i <= k; i ++) {
        cout << seq[i] << " ";
    }

    return 0;
}
posted @ 2026-02-25 10:39  KenopsiaMind  阅读(4)  评论(0)    收藏  举报