AtCoder Grand Contest 032 D Rotation Sort

发布时间 2023-07-18 10:25:27作者: zltzlt

洛谷传送门

AtCoder 传送门

\(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;
}