ARC080E Young Maids


2019-04-14

原题链接

题意大概就是给你一个排列 \(p\),你每次可以找到 \(p\) 中相邻两个数并将其移至另一个初始为空的队列的开头,让最终 \(p\) 的字典序尽量小。

过程大概就是这样:

题解

由于字典序大小是从前往后决定的,所以我们考虑从前往后确定这个序列,也就是将题意中的过程倒着做。

我们考虑在某一状态下,选择的两个数在原序列中是 \(p_l,p_r\),那么再次之前的选择中,不可能出现选择的数为 \(p_{a_l},p_{a_r}\) 使得 \(a_l<l<a_r\)\(a_l<r<a_r\)

于是我们发现倒着考虑是,选择 \(p_l,p_r\) 时就是把序列分成了 \([1,l-1],[l+1,r-1],[r+1,n]\)\(3\) 段,这 \(3\) 段相互独立。

我们考虑怎样的 \(<l,r>\) 是合法的。显然就是 \([1,l-1],[l+1,r-1],[r+1,n]\) 长度均为偶数时(可能为空)。

所以 \(l\) 一定为奇数,\(r\) 一定为偶数。

我们可以维护出 \(p\) 中奇数位于偶数位的最小值,并用一个小根堆维护每一个 \([l,r]\) 的最小值即可,每次选择后将 \([l,r]\) 分成 \(3\) 段重新放入堆中。

代码

cpp
#include <bits/stdc++.h>

using namespace std;

// 读入输出优化略去

const int maxn = 200005;
const int inf = 0x3f3f3f3f;

int n;
int xx[maxn]; // 题意中的p

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

struct Tree
{
    struct Node
    {
        int jx, ox; // 奇数位的最小值与偶数位的最小值
    } no[maxn << 2];

    int k;

    inline int minn(int a, int b)
    {
        if (!a || !b)
            return a | b;
        else
            return xx[a] < xx[b] ? a : b;
    }

    inline void push_up(int k)
    {
        no[k].jx = minn(no[ls(k)].jx, no[rs(k)].jx);
        no[k].ox = minn(no[ls(k)].ox, no[rs(k)].ox);
    }

    inline void build_tree(int n)
    {
        for (k = 1; k <= n; k <<= 1);
        for (int i = 1; i <= n; ++i)
        {
            if (i & 1)
                no[i + k].jx = i;
            else
                no[i + k].ox = i;
        }
        for (int i = k; i; --i)
            push_up(i);
    }

    inline int query(int l, int r, int kk)
    {
        int ans = 0;
        for (l += k - 1, r += k + 1; l ^ r ^ 1; l >>= 1, r >>= 1)
        {
            if (~l & 1)
                ans = minn(kk ? no[l ^ 1].jx : no[l ^ 1].ox, ans);
            if (r & 1)
                ans = minn(kk ? no[r ^ 1].jx : no[r ^ 1].ox, ans);
        }
        return ans;
    }
} tr;

struct QJ
{
    int l, r, ansl, ansr; // 现在可选区间为[l,r],最优是选择p[l]和p[r]

    friend bool operator < (QJ a, QJ b)
    {
        return xx[a.ansl] > xx[b.ansl];
    }

    QJ (int l, int r)
    {
        this->l = l;
        this->r = r;
        this->ansl = tr.query(l, r, 1);
        this->ansr = tr.query(ansl + 1, r, 0);
    }
};

priority_queue<QJ> q;

int main()
{
    read(n);
    for (int i = 1; i <= n; ++i)
        read(xx[i]);
    tr.build_tree(n);
    q.push(QJ(1, n));
    while (!q.empty())
    {
        QJ now = q.top();
        q.pop();
        writesp(xx[now.ansl]);
        writesp(xx[now.ansr]);
        if (now.ansl + 1 < now.ansr - 1)
            q.push(QJ(now.ansl + 1, now.ansr - 1));
        if (now.l < now.ansl - 1)
            q.push(QJ(now.l, now.ansl - 1));
        if (now.ansr + 1 < now.r)
            q.push(QJ(now.ansr + 1, now.r));
    }
    return 0;
}

Cite this post

@misc{pu2019arc080c,
  author = {Pu, Fanyi},
  title  = {ARC080E Young Maids},
  year   = {2019},
  month  = {4},
  url    = {https://pufanyi.com/blog/arc080-c}
}