1 条题解

  • 0
    @ 2026-5-14 20:17:34

    题解

    题意简述

    给定一个部分缺失的排列 aa00 表示缺失),需要填充缺失的数字(1n1 \sim n 各一次)得到排列 bb
    定义 f(c)f(c) 为 Turtle 和 Piggy 在序列 cc 上按照规则(Turtle 取 min\min,Piggy 取 max\max)轮流操作,最终 c1c_1 的值(双方最优)。
    排列 bb 的美丽值为所有非空子段的 ff 值之和。
    求最大可能的美丽值。


    思路分析

    1. f(c)f(c) 的封闭形式

    通过归纳可证(见原题解简述),对于长度为 kk 的序列 cc

    • kk 为奇数:f(c)=max(c1,ck)f(c) = \max(c_1, c_k)
    • kk 为偶数:f(c)=min(c1,ck)f(c) = \min(c_1, c_k)

    对称地,若交换先后手(即 Piggy 先手),结果 g(c)g(c) 为:

    • kk 奇数:g(c)=min(c1,ck)g(c) = \min(c_1, c_k)
    • kk 偶数:g(c)=max(c1,ck)g(c) = \max(c_1, c_k)

    这个结论是解题的关键。


    2. 将美丽值转化为求和

    对于排列 pp,美丽值:

    $$\text{beauty}(p) = \sum_{i=1}^n \sum_{j=i}^n f([p_i, \dots, p_j]) $$

    设子段长度 len=ji+1len = j-i+1。由 ff 的公式:

    • lenlen 为奇数(即 iijj 奇偶性相同):贡献 max(pi,pj)\max(p_i, p_j)
    • lenlen 为偶数(即 iijj 奇偶性不同):贡献 min(pi,pj)\min(p_i, p_j)

    于是:

    $$\text{beauty}(p) = \sum_{i=1}^n \sum_{j=i}^n \left( [i\equiv j \pmod{2}] \max(p_i,p_j) + [i\not\equiv j \pmod{2}] \min(p_i,p_j) \right) $$

    注意到:

    $$\max(x,y) = \frac{x+y+|x-y|}{2},\quad \min(x,y) = \frac{x+y-|x-y|}{2} $$

    但更直接地,我们有恒等式:

    $$\sum_{i=1}^n \sum_{j=i}^n \max(p_i,p_j) = \sum_{i=1}^n \sum_{j=i}^n \min(p_i,p_j) + \sum_{i=1}^n \sum_{j=i}^n |p_i-p_j| \cdot [i\not\equiv j] $$

    不过原题解给出了一个更简洁的推导:

    最终可得:

    $$\text{beauty}(p) = \sum_{i=1}^n i^2 - \sum_{i=1}^n \sum_{j=i+1}^n [i\not\equiv j \pmod{2}] \cdot |p_i - p_j| $$

    证明:i=1nj=inmax(pi,pj)\sum_{i=1}^n \sum_{j=i}^n \max(p_i,p_j)i2\sum i^2 的关系,结合奇偶分类。

    因此,最大化美丽值等价于最小化:

    $$S = \sum_{i=1}^n \sum_{j=i+1}^n [i\not\equiv j \pmod{2}] \cdot |p_i - p_j| $$

    3. 转化为染色问题

    将位置 1..n1..n 按奇偶染色:

    • 奇数位置:黑色
    • 偶数位置:白色

    已知 aa 中某些位置的值固定,这些值对应的位置奇偶性也固定(因为值是 1..n1..n 但位置是固定的,注意:这里混淆了!实际上值 xx 在排列中出现在某个位置,但我们现在要考虑的是位置的奇偶性,而不是值的奇偶性。)

    关键:SS所有不同色位置对之间的数值差的绝对值之和。
    因为 [i≢j][i\not\equiv j] 正是不同色。

    所以问题变为:

    • 我们有 nn 个位置,每个位置最终会放一个 1..n1..n 的数(排列)。
    • 但位置已经按奇偶分好色(黑 = 奇数下标,白 = 偶数下标)。
    • 某些位置的值已经固定(由 aa 非零给出)。
    • 需要把剩下的数分配到未定位置,使得不同色位置对的数值差的绝对值之和最小。

    4. 重新表述问题

    BB = 黑色位置集合(大小为 n/2\lceil n/2 \rceil),WW = 白色位置集合(大小为 n/2\lfloor n/2 \rfloor)。
    目标:将 1..n1..n 分配到 BWB \cup W,满足固定约束,最小化:

    bBwWpbpw\sum_{b \in B} \sum_{w \in W} |p_b - p_w|

    这等价于:对于每个数值 xx,它要么在黑色位置,要么在白色位置。我们需要最小化所有异色对数值差的绝对值之和。

    另一种常见技巧:按数值从小到大排序,考虑相邻数值间的贡献。
    设排序后的数值为 v1<v2<<vnv_1 < v_2 < \dots < v_n,每个 vtv_t 有一个颜色(黑或白)。
    那么异色对的总和可以写作:

    $$\sum_{t=1}^{n-1} (v_{t+1} - v_t) \cdot (\text{前 $t$ 个中黑个数} \cdot \text{后 $n-t$ 中白个数} + \text{前 $t$ 个中白个数} \cdot \text{后 $n-t$ 中黑个数}) $$

    这个形式可以用 DP 处理:按数值从小到大决定颜色,同时维护已选的黑白个数。


    5. DP 设计

    f[i][j]f[i][j] 表示已经考虑了最小的 ii 个数,其中 jj 个被涂成黑色,此时的最小异色对部分和(即上述公式中已经累加的贡献)。

    初始化:f[0][0]=0f[0][0] = 0

    当考虑第 i+1i+1 个数时,它的颜色由位置决定吗?不,这里数值的顺序是固定的 1..n1..n,但每个数值必须对应某个实际位置。我们实际上是在按数值顺序决定它的颜色,但颜色的可用数量受位置奇偶约束。

    更准确:已知某些数值必须出现在奇数位置(黑)或偶数位置(白),或者自由。
    我们按数值 x=1..nx=1..n 顺序 DP:

    • 如果 xx 必须黑:jj 增加 1,贡献加上 $j \cdot (W_{\text{剩余}}) + (i-j) \cdot (B_{\text{剩余}})$ 的一部分? 原题解中,贡献是按位置下标计算的,而不是按数值顺序。他们使用了另一视角:枚举位置 i=1..ni=1..n,决定该位置放哪个数。

    实际上标程的做法是:

    按位置 ii11nn 顺序 DP,f[i][j]f[i][j] 表示前 ii 个位置中恰好有 jj 个黑色位置已被分配数值,此时的最小异色对部分和(只考虑这些已分配位置之间的异色对,以及它们与尚未分配位置之间的某种预计算贡献)。

    他们使用了一个巧妙的累加方式:当处理到位置 ii 时,先加上一个“固定贡献”,这个贡献与 ii 及当前黑白数量有关。具体为:

    $$\text{add} = j \cdot (W_{\text{总}} - (i-j)) + (i-j) \cdot (B_{\text{总}} - j) $$

    其中 B=n/2B_{\text{总}} = \lceil n/2 \rceilW=n/2W_{\text{总}} = \lfloor n/2 \rfloor

    这个加法的含义:考虑当前位置 ii(即第 ii 个位置)与所有未来位置的异色对?需要仔细理解原题解。


    6. 最终公式

    经过推导,最终答案是:

    $$\text{ans} = \sum_{i=1}^n i^2 - \min_{\text{合法染色}} \sum_{i=1}^n \sum_{j=i+1}^n [i\not\equiv j] |p_i - p_j| $$

    而最小值通过上述 DP 求得 f[n][B]f[n][B_{\text{总}}],然后:

    $$\text{ans} = \sum_{i=1}^n i^2 - f[n][\lceil n/2 \rceil] $$

    7. 算法复杂度

    DP 状态 O(n2)O(n^2),转移 O(1)O(1),总复杂度 O(n2)O(n^2)nn 总和 5000\le 5000,可行。


    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    void solve() {
        int n;
        cin >> n;
        vector<int> a(n + 1, -1);
        for (int i = 1, x; i <= n; ++i) {
            cin >> x;
            if (x) a[x] = i & 1;   // 数值 x 必须出现在奇偶性为 i&1 的位置
        }
        const ll INF = 1e18;
        vector<vector<ll>> f(n + 1, vector<ll>(n + 1, INF));
        if (a[1] != 1) f[1][0] = 0;   // 数值1不是白色位置(即可以放奇数位置?这里注意:a[1]记录的是数值1的强制颜色?)
        if (a[1] != 0) f[1][1] = 0;   // 数值1可以放黑色位置
        // 上面这个初始化实际意思是:数值1可以放在奇数位置(黑)或偶数位置(白),但若被强制则只能选对应位置。
        // 更准确:a[x] = 0 表示数值 x 必须出现在偶数位置(白),=1 表示必须出现在奇数位置(黑),=-1 表示自由。
        int B = (n + 1) / 2, W = n / 2;   // 黑位置数,白位置数
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j <= i; ++j) if (f[i][j] < INF) {
                // 加上位置 i 与所有未来位置的贡献(这里 i 是数值顺序还是位置顺序?原题解中 i 是数值顺序?)
                // 原标程中 i 是位置下标?不,这里的 i 是已经分配了多少个数?需要重新梳理
                // 这里我们直接按原标程理解:f[i][j] 表示考虑了数值 1..i,其中 j 个被涂黑时的最小代价
                // 当前要加上数值 i 与后面所有数值的异色对贡献的一部分。
                // 其实原题解公式:add = j * (W - (i - j)) + (i - j) * (B - j)
                // 它的含义:前 i 个数中,j 个黑,(i-j)个白。后面还有 B-j 个黑,W-(i-j) 个白。
                // 数值 i 与后面每一个异色对都会产生 |v_i - v_{后面}| 的贡献,但这里累加的是按数值顺序的差分和。
                // 实际上这是数值排序后的经典贡献累加方式:按数值递增顺序,相邻差乘以前后异色对计数。
                // 但为了方便,直接按原标程实现即可。
                ll add = 1LL * j * (W - (i - j)) + 1LL * (i - j) * (B - j);
                f[i][j] += add;
                // 下一个数值 i+1 的颜色选择
                if (a[i + 1] != 1) // 可以涂白(偶数位置)
                    f[i + 1][j] = min(f[i + 1][j], f[i][j]);
                if (a[i + 1] != 0) // 可以涂黑(奇数位置)
                    f[i + 1][j + 1] = min(f[i + 1][j + 1], f[i][j]);
            }
        }
        ll ans = -f[n][B];
        for (int i = 1; i <= n; ++i) ans += 1LL * i * i;
        cout << ans << '\n';
    }
    
    int main() {
        ios::sync_with_stdio(0); cin.tie(0);
        int T; cin >> T;
        while (T--) solve();
        return 0;
    }
    
    • 1

    信息

    ID
    7076
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者