Loading

[Baekjoon 19064] Readability

前言

\(\rm{subtask}\) 但是基本没有部分分做法
那请问你怎么做, 随机化都不让过???

算了不到啊, 毕竟可能都不是出题人的题解, 可能人家不喜欢随机
至少策略什么没出大锅, 继续加油

思路

不难发现按照顺序排列一定可以产生一种合法的解

性质

考虑把原序列转化成这么一个合法解的过程
pEMgv1x.png

具体处理

先按照原串中的顺序构造一组开销最优解, 不妨记为 QQ , 此时每个数的移动方向确定, 也就是到达 QQ 的方向确定
不妨记 posipos_iQiQ_i 在原串中的位置

考虑交换 QQ 中的两个位置

假设交换 Qi,QjQ_i, Q_j , 其中满足 i<j,Qi>Qji < j, Q_i > Q_j
什么时候交换是合法的? ((即不会影响开销))
不难发现如果 (i,j)(i, j)(posi,posj) or (posj,posi)(pos_i, pos_j) \textrm{ or } (pos_j, pos_i) 没有交, 那么合法

但是这样仍然不好处理, 我们先继续维护

假设当前考虑奇数, 我们记 posipos_i 表示第 ii 个奇数在原串中的位置, qiq_i 表示第 ii 个奇数在 QQ 中的位置
观察到如果有一些连续的 posi</>qipos_i </> q_i , 才可能出现连续的交换, 所以我们一次维护一个极长的这样的段, 也就是极长的移动方向相同的串

继续假设当前是连续的 posi>qipos_i > q_i , 即全部向左走
动态对于第 ii 个奇数维护一个类似
pEM2LqS.png
的极大段

那么对于这个极大段, 不难发现可以任意交换形成连通块
所以我们动态维护包含当前位置的极长段中的元素, 贪心的选择最大的放在后面即可

其他情况类似处理即可

pEMbyRS.png

具体怎么实现?

对于每一个位置动态维护其在那个段中 ((覆盖全串的那个段)) , 然后贪心选即可
难以发现如果一个位置的最远点 ((覆盖全串的那个段的最右边)) 覆盖了当前位置, 那么当前位置就在这个段中, 堆模拟即可


总结一下
不难发现最终的结果应该从 \(Q\)\((\)也就是按照顺序分配的段\()\) 交换而来
进一步发现连通块的性质, 然后在连通块中贪心处理字典序

其他就是些显然的分类讨论, 虽然题解代码非常漂亮, 但在这里不加赘述

最后一个问题是如何

实现

#include <bits/stdc++.h>
using namespace std;

// 宏定义,简化循环代码
#define rep(i, j, k) for (int i = (j); i <= (k); i++)
#define per(i, j, k) for (int i = (j); i >= (k); i--)

const int N = 114514; // 定义数组的最大大小

// 变量声明
int ty, a1, a2, n, a[N], b[N], c[N], as[2][N], p[N], in[N], f[N], g[N];
priority_queue<int> q; // 优先队列,用于贪心选择
set<int> s; // 集合,用于维护当前可用的位置

// work函数:处理一段连续的元素,调整它们的顺序以最小化字典序
void work(int l, int r, int x) {
    int nw;
    if (x) { // 如果元素需要向左移动
        per(i, r, l) { // 从右向左遍历
            s.insert(p[i]); // 将当前位置插入集合
            while (*s.rbegin() > in[i]) s.erase(*s.rbegin()); // 移除不满足条件的位置
            f[i] = *s.rbegin(); // 记录当前可用的最大位置
        }
        nw = r;
        per(i, r, l) { // 再次从右向左遍历
            while (nw >= l && f[nw] >= p[i]) q.push(b[in[nw]]), nw--; // 将满足条件的元素加入优先队列
            g[i] = q.top(); // 选择最大的元素
            q.pop(); // 移除已选择的元素
        }
    } else { // 如果元素需要向右移动
        rep(i, l, r) { // 从左向右遍历
            s.insert(p[i]); // 将当前位置插入集合
            while (*s.begin() < in[i]) s.erase(s.begin()); // 移除不满足条件的位置
            f[i] = *s.begin(); // 记录当前可用的最小位置
        }
        nw = l;
        rep(i, l, r) { // 再次从左向右遍历
            while (nw <= r && f[nw] <= p[i]) q.push(-b[in[nw]]), nw++; // 将满足条件的元素加入优先队列
            g[i] = -q.top(); // 选择最小的元素
            q.pop(); // 移除已选择的元素
        }
    }
    s.clear(); // 清空集合
    while (!q.empty()) q.pop(); // 清空优先队列
}

// get函数:计算将奇数或偶数元素分配到目标位置的最小代价
int get(int x) {
    int ct = 0;
    rep(i, 1, n) if (a[i]) { // 遍历数组,统计奇数或偶数的数量
        ct++;
        p[ct] = 2 * ct - 1 + x; // 计算目标位置
        in[ct] = i; // 记录原始位置
    }
    if (ct == 0) return 0; // 如果没有奇数或偶数,返回0
    if (p[ct] > n) return 1e9; // 如果目标位置超出范围,返回一个大数
    for (int i = 1, j; i <= ct; i = j + 1) { // 分段处理
        j = i;
        if (in[i] == p[i]) { // 如果元素已经在目标位置
            as[ty][p[i]] = b[in[i]]; // 直接赋值
            continue;
        }
        while (j < ct && (p[j + 1] < in[j + 1]) == (p[i] < in[i])) j++; // 找到连续的一段
        work(i, j, p[i] < in[i]); // 调用work函数处理这一段
        rep(l, i, j) as[ty][p[l]] = g[l]; // 将处理后的结果赋值
    }
    int tt = 0;
    rep(i, 1, ct) tt += abs(p[i] - in[i]); // 计算总代价
    return tt;
}

// sol函数:处理两种排列方式(奇数开头和偶数开头),返回最小代价
int sol(int x) {
    int tt = 0;
    rep(i, 1, n) a[i] = (b[i] & 1); // 标记奇数
    tt += get(x); // 计算奇数部分的代价
    rep(i, 1, n) a[i] ^= 1; // 标记偶数
    tt += get(x ^ 1); // 计算偶数部分的代价
    return tt;
}

// 主函数
int main() {
    scanf("%d", &n); // 读取序列长度
    rep(i, 1, n) scanf("%d", &b[i]); // 读取序列元素
    a1 = sol(0); ty = 1; // 处理奇数开头的排列
    a2 = sol(1); // 处理偶数开头的排列
    if (a2 < a1 || (a2 == a1 && as[1][1] < as[0][1])) swap(as[0], as[1]); // 选择代价更小或字典序更小的排列
    rep(i, 1, n) printf("%d ", as[0][i]); // 输出结果
}

总结

  • 定义操作 (约束) 和开销 / 收益, 要求最值化开销 / 收益
    • 推式子计算约束条件
    • 模拟操作情况, 找到最好开销, 注意最大和最小, 一般来说可以贪贪心 (将简单情况先处理, 然后在基础上处理最值)
    • 考虑操作对答案的影响 (推式子) , 据此对操作进行排序
    • 往往利用 \(\rm{dp}\) , 结合约束处理当前方案
    • 列出合法情况需要满足的表达式, 在原序列中贪心选择最优情况

操作类贪心, 考虑转化成连通块的问题, 方便处理
把区间贪心转化为每个位置可以得到的最优的数是一个非常巧妙的实现

posted @ 2025-02-18 21:06  Yorg  阅读(29)  评论(0)    收藏  举报