[AGC032D] Rotation Sort 题解
[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;
}

        Reimu
    
                
            
        
浙公网安备 33010602011771号