1 条题解
-
0
题解
题意简述
给定一个部分缺失的排列 ( 表示缺失),需要填充缺失的数字( 各一次)得到排列 。
定义 为 Turtle 和 Piggy 在序列 上按照规则(Turtle 取 ,Piggy 取 )轮流操作,最终 的值(双方最优)。
排列 的美丽值为所有非空子段的 值之和。
求最大可能的美丽值。
思路分析
1. 的封闭形式
通过归纳可证(见原题解简述),对于长度为 的序列 :
- 若 为奇数:
- 若 为偶数:
对称地,若交换先后手(即 Piggy 先手),结果 为:
- 奇数:
- 偶数:
这个结论是解题的关键。
2. 将美丽值转化为求和
对于排列 ,美丽值:
$$\text{beauty}(p) = \sum_{i=1}^n \sum_{j=i}^n f([p_i, \dots, 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| $$证明: 与 的关系,结合奇偶分类。
因此,最大化美丽值等价于最小化:
$$S = \sum_{i=1}^n \sum_{j=i+1}^n [i\not\equiv j \pmod{2}] \cdot |p_i - p_j| $$
3. 转化为染色问题
将位置 按奇偶染色:
- 奇数位置:黑色
- 偶数位置:白色
已知 中某些位置的值固定,这些值对应的位置奇偶性也固定(因为值是 但位置是固定的,注意:这里混淆了!实际上值 在排列中出现在某个位置,但我们现在要考虑的是位置的奇偶性,而不是值的奇偶性。)
关键: 是所有不同色位置对之间的数值差的绝对值之和。
因为 正是不同色。所以问题变为:
- 我们有 个位置,每个位置最终会放一个 的数(排列)。
- 但位置已经按奇偶分好色(黑 = 奇数下标,白 = 偶数下标)。
- 某些位置的值已经固定(由 非零给出)。
- 需要把剩下的数分配到未定位置,使得不同色位置对的数值差的绝对值之和最小。
4. 重新表述问题
设 = 黑色位置集合(大小为 ), = 白色位置集合(大小为 )。
目标:将 分配到 ,满足固定约束,最小化:这等价于:对于每个数值 ,它要么在黑色位置,要么在白色位置。我们需要最小化所有异色对数值差的绝对值之和。
另一种常见技巧:按数值从小到大排序,考虑相邻数值间的贡献。
$$\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 设计
设 表示已经考虑了最小的 个数,其中 个被涂成黑色,此时的最小异色对部分和(即上述公式中已经累加的贡献)。
初始化:。
当考虑第 个数时,它的颜色由位置决定吗?不,这里数值的顺序是固定的 ,但每个数值必须对应某个实际位置。我们实际上是在按数值顺序决定它的颜色,但颜色的可用数量受位置奇偶约束。
更准确:已知某些数值必须出现在奇数位置(黑)或偶数位置(白),或者自由。
我们按数值 顺序 DP:- 如果 必须黑: 增加 1,贡献加上 $j \cdot (W_{\text{剩余}}) + (i-j) \cdot (B_{\text{剩余}})$ 的一部分? 原题解中,贡献是按位置下标计算的,而不是按数值顺序。他们使用了另一视角:枚举位置 ,决定该位置放哪个数。
实际上标程的做法是:
按位置 从 到 顺序 DP, 表示前 个位置中恰好有 个黑色位置已被分配数值,此时的最小异色对部分和(只考虑这些已分配位置之间的异色对,以及它们与尚未分配位置之间的某种预计算贡献)。
他们使用了一个巧妙的累加方式:当处理到位置 时,先加上一个“固定贡献”,这个贡献与 及当前黑白数量有关。具体为:
$$\text{add} = j \cdot (W_{\text{总}} - (i-j)) + (i-j) \cdot (B_{\text{总}} - j) $$其中 ,。
这个加法的含义:考虑当前位置 (即第 个位置)与所有未来位置的异色对?需要仔细理解原题解。
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 求得 ,然后:
$$\text{ans} = \sum_{i=1}^n i^2 - f[n][\lceil n/2 \rceil] $$
7. 算法复杂度
DP 状态 ,转移 ,总复杂度 , 总和 ,可行。
代码
#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
- 上传者