Loading

AtCoder-abc262_f Erase and Rotate

Erase and Rotate

思维 + 单调栈

  1. 考虑只能删除的情况:

由于我们要求字典序最小,因此考虑在前 \(k + 1\) 个数字中,我们选取最小的,然后把在他前面的所有数字全部删掉,每删掉一个都让 \(k\) 减小 \(1\)

然后保留那个最小的数字,往后继续上述过程

如果跑到最后仍有 \(k > 0\),则考虑删掉后面 \(k\) 个数字

这个过程可以用单调栈 \(O(n)\) 实现,每次弹出一个数字则让 \(k-1\)

  1. 考虑能够翻转的情况

贪心地想,我们找到一个字典序最小的,并且能够让其翻转到最前面来的数字,然后把它本身以及它后面的全部数字翻转到前面来,再进行删除操作

因为翻转本身就消耗了一点,因此删除被翻转到前面的数字时,不需要消耗能量

但是上述情况并不是最优,有可能无需翻转,直接删除就可以找到最优,因此要考虑这两种情况最后的出来的最小字典序作为答案

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 2e5 + 10;
int num[maxn], alp[maxn];
int n;

vector<int> solve(int k, int r = -1)
{
    vector<int>q;
    for(int i=1; i<=n; i++)
    {
        while(q.size() && num[q.back()] > num[i] && (k || q.back() <= r))
        {
            if(q.back() > r) k--;
            q.pop_back();
        }
        q.push_back(i);
    }
    for(int i=0; i<k; i++)
    {
        if(alp[q.back()] <= r) k++;
        q.pop_back();
    }
    for(int i=0; i<q.size(); i++) q[i] = num[q[i]];
    return q;
}

void print(const vector<int> &x)
{
    int cur = 0;
    for(int i=0; i<x.size(); i++)
    {
        if(cur++) cout << " ";
        cout << x[i];
    }
    cout << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int k;
    cin >> n >> k;
    for(int i=1; i<=n; i++)
    {
        cin >> num[i];
        alp[num[i]] = i;
    }
    auto a = solve(k);
    int s = 0;
    for(int i=1; i<=n; i++)
    {
        if(n - alp[i] + 1 <= k)
        {
            s = n - alp[i] + 1;
            for(int j=1; j<=n; j++)
                alp[j] = (alp[j] - 1 + s) % n + 1;
            break;
        }
    }
    for(int i=1; i<=n; i++) num[alp[i]] = i;
    auto b = solve(k - s, s);
    auto ans = min(a, b);
    print(ans);
    return 0;
}
posted @ 2022-08-05 20:41  dgsvygd  阅读(84)  评论(0编辑  收藏  举报