Loading

[??记录]abc262F Erase and Rotate

不是特别难的思维题。

打完 E 还剩 1h,如果专心想说不定能想出来,然而看到通过人数仅 172 就放弃了,这是应杜绝的。


题意:给定一个排列 \(\{a_n\}\),有两种操作:

  • 将最后数提到最前(Rotate)

  • 删除一个数(Erase)

求操作 \(k\) 次后最小字典序。


最小字典序看着是贪心。如果只有旋转,选最后 \(k\) 个数中最小数与 \(a_1\) 作比较。如果只有删除,迭代:在前 \(k+1\) 个数中选取最小数 \(a_i\),删去 \(a_1-a_{i-1}\),然后把 \(k\) 减去 \(i-1\),对第二个数做一样的操作。如果到最后操作有剩余,就从后往前删。用线段树能做到 \(O(n \log n)\),用单调栈能做到 \(O(n)\)

这给我们的启示是第一个数的选择其实不多。现在考虑至少旋转一次的情况。

为了更好讨论,我们发现:旋转后删除与旋转前删除效果一致,所以强制先旋转再删除,且旋转后删除不占用操作次数。同时,我们应该让旋转过去的最后一个数不被删除,为防止一个情况被多次讨论。

那么就明晰了:只用讨论旋转过去后第一个数为从后往前 \(k\) 数中最小数的情况。此时再做删除操作,复杂度同最前分析。

于是我们就得到了解法。其中关键的一步是统一了旋转与删除,即“先旋转后删除”。之后发现旋转过去后的数一定只是顺水推舟。先分别讨论了只有一种操作的情况也是一个重要的铺垫。

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int M = 200005;
int n, a[M], k, cost[M], b[M], pl;
struct node{
    int mnd, mnp;
    bool operator < (const node &t) const{
        return mnd < t.mnd;
    }
};
node min(node x, node y) {return x < y ? x : y;}
struct sg2{
    node s[M << 2];
    void clear(){
        memset(s, 0, sizeof(s));
    }
    void build(int o, int l, int r){
        if(l == r) {s[o] = {a[l], l}; return;}
        int mid = l + r >> 1;
        build(o<<1, l, mid); build(o<<1|1, mid+1, r);
        s[o] = min(s[o<<1], s[o<<1|1]);
    }
	node query(int o, int l, int r, int x, int y){
		if(x <= l && r <= y) return s[o];
		int mid = l + r >> 1; node ans = {(int)1e9, (int)1e9};
		if(x <= mid) ans = min(ans, query(o<<1, l, mid, x, y));
		if(y > mid) ans = min(ans, query(o<<1|1, mid+1, r, x, y));
		return ans;
	}
}t;
void print(vector<int> t){
    for(auto i : t) printf("%d ", i);
    puts("");
}
vector<int> solve(){
    vector<int> ans; t.clear(); t.build(1, 1, n);
    int l = 1, c = k;
    while(l <= n){
        int tmpp = t.query(1, 1, n, l, max(l+c, n-pl+2+c)).mnp;
        // printf("tmpp = %d c = %d l = %d\n", tmpp, c, l);
        for(int i = l; i < tmpp; i++) c -= cost[i];
        ans.push_back(a[tmpp]); l = tmpp + 1;
    }
    for(int i = l; i <= n; i++) ans.push_back(a[i]);
    for(int i = 1; i <= c; i++) ans.pop_back();
    return ans;
}
int main(){
    scanf("%d %d", &n, &k); pl = 100000000;
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]); cost[i] = 1;
    }
    vector<int> ans1 = solve();
    // print(ans1);
    if(k == 0) {print(ans1); return 0;}
    pl = t.query(1, 1, n, n-k+1, n).mnp;
    for(int i = 1; i <= n-pl+1; i++) b[i] = a[i+pl-1], cost[i] = 0;
    for(int i = 1; i < pl; i++) b[i+n-pl+1] = a[i];
    for(int i = 1; i <= n; i++) a[i] = b[i];
    k -= n-pl+1;
    // lim = n-pl+1;
    vector<int> ans2 = solve();
    // print(ans2);
    vector<int> ans = min(ans1, ans2);
    print(ans); return 0;
}
posted @ 2022-08-09 11:22  purplevine  阅读(53)  评论(0编辑  收藏  举报