[AGC032D] Rotation Sort 题解
把循环移位看作是将某个数向左或右插入到任意位置,显然一个数最多被移动一次。
那么该序列中一共有三种数:
- 向左移动
- 向右移动
- 不动
假设已知每个数属于哪一种,考虑如何判定该方案是否合法:
- 不动的数一定是单调递增的
- 不动的数把序列划分成了若干段,考虑一段内的数,对于向左移动的,要求其后一个不动的数大于它
- 对于向右移动的,要求其前一个不动的数小于它
如果一个数前一个不动的数小于它,后一个不动的数大于它,那么它可以直接不动,且容易验证进行这样的约束之后剩下部分依旧满足上述条件。
比如 \(x < y < z, a_x < a_y < a_z\),我们把 \(y\) 变成不动的,则 \(p\in(x, y), a_p > a_z\rightarrow a_p > a_y\),\(p\in(y,z), a_p < a_x\rightarrow a_p < a_y\)。
所以一个合法方案形如若干单调递增的数作为不动数,相邻不动数中间的一段满足 2. 或 3. 其中之一,这样一个段内数的操作代价,只需要和其前后的不动数其中一个作比较就可知。
得到了方案的刻画,进行 DP,\(f_i\) 表示前 \(i\) 个数,钦定 \(i\) 作为不动数的方案数,转移枚举上一个不动数即可。
时间复杂度:\(O(n^2)\)。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;int n, a, b, p[N], f[N];signed main() {ios::sync_with_stdio(0), cin.tie(0);cin >> n >> a >> b;for(int i = 1; i <= n; i ++) cin >> p[i];f[0] = 0;int ans = 1e18;p[n + 1] = 1e9;for(int i = 1; i <= n + 1; i ++){f[i] = 1e18;for(int j = i - 1, c = 0; ~j; j --) {if(p[j] < p[i]) f[i] = min(f[i], f[j] + c);if(p[j] > p[i]) c += a;else c += b;}}cout << f[n + 1] << '\n';return 0;
}