[??记录]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;
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/16565316.html