AtCoder Grand Contest 032 D Rotation Sort
设 \(b_i\) 为 \(i\) 在排列 \(p\) 中的位置。那么每次操作相当于选择一个 \(p_i\),将 \(b_{p_i}\) 增加至 \(k \in [i + 1, n]\),\(b_{p_{i + 1}}, b_{p_{i + 2}}, \ldots, b_{p_k}\) 减少 \(1\),花费 \(A\);或者将 \(b_{p_i}\) 减少至 \(k \in [1, i - 1]\),\(b_{p_{i - 1}}, b_{p_{i - 2}}, \ldots, b_{p_k}\) 增加 \(1\),花费 \(B\)。
这样看起来还不是很好做,因为一次操作的影响太大了。注意到我们只关心 \(b_i\) 的相对大小关系,\(b_i\) 具体是什么不重要,因此我们考虑改变一下操作的描述:可以将 \(b_{p_i}\) 增加或减少至任意实数 \(k \in [0, n + 1)\),花费分别为 \(A, B\)。
至此容易知道每个 \(b_i\) 最多改变一次,且我们的目标是使得 \(b\) 递增。容易设计一个 dp,\(f_{i, j}\) 为操作后 \(b_i \in [j, j + 1)\) 的花费最小值。
有转移:
- \(\forall k \in [0, j], f_{i, j} \gets f_{i - 1, k} + \begin{cases} A & b_i \le j \\ B & b_i > j \end{cases}\),表示 \(b_i\) 取 \((j, j + 1)\) 的小数。
- \(\forall k \in [0, j - 1], f_{i, j} \gets f_{i - 1, k} + \begin{cases} A & b_i < j \\ 0 & b_i = j \\ B & b_i > j \end{cases}\),表示 \(b_i\) 取整数 \(j\)。
容易维护 \(f_i\) 的前缀 \(\min\) 做到 \(O(n^2)\)。
code
// Problem: D - Rotation Sort
// Contest: AtCoder - AtCoder Grand Contest 032
// URL: https://atcoder.jp/contests/agc032/tasks/agc032_d
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 5050;
ll n, A, B, a[maxn], b[maxn], f[maxn][maxn], g[maxn][maxn];
void solve() {
scanf("%lld%lld%lld", &n, &A, &B);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
b[a[i]] = i;
}
mems(f, 0x3f);
mems(g, 0x3f);
f[0][0] = 0;
for (int i = 0; i <= n; ++i) {
g[0][i] = 0;
}
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= n; ++j) {
f[i][j] = g[i - 1][j] + (b[i] <= j ? A : B);
f[i][j] = min(f[i][j], g[i - 1][j - 1] + (b[i] < j ? A : (b[i] > j ? B : 0)));
}
g[i][0] = f[i][0];
for (int j = 1; j <= n; ++j) {
g[i][j] = min(g[i][j - 1], f[i][j]);
}
}
printf("%lld\n", g[n][n]);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}