@wsyear强无敌,模拟赛场切黑题。
给你一个长度为 的排列,Alice 先会把它分成 段。
然后 Bob 会把这 段重新排列,而每段内部位置不变。
Alice 希望这个序列字典序最小,而 Bob 希望这个序列字典序最大。
输出重排后的序列。
考虑贪心。
我们发现字典序一定不会比原来的序列小,所以我们要让不变的前缀尽量长。
很明显这个前缀的长度是有单调性的,所以可以考虑二分。
判断是否可行:
假设你现在二分到的长度是 。
我们分的 段中有两种,一种是在 中的,一种是在 中的。
对于在 中的段,我们发现他们的开头是单调递减的,否则Bob可以把开头较大的移动到前面。
对于 的段,每一段的开头都要比 中的开头小,否则你可以把开头大的移到前面。
考虑枚举 中的段数 ,然后求出最后一段开头的最大值 ,那么只要找出 中有多少个数(假设有 个)可以作为开头(也就是小于 ),如果 那么就可行。
接下来要求出 :
这里的思路参考了 P4577 [FJOI2018]领导集团问题 的思路。
维护一个set
,从大到小排,其中第 个元素表示长度为 的递减子序列的最大结尾。
考虑加入一个数 ,那么找到他的后继 ,发现以 结尾和以 结尾的最长下降子序列长度是一样的,而 ,所以可以把 删除。
现在求出了最长的不变的前缀 ,我们现在就只要考虑 应该怎么分比较好。
首先你在 分的段数一定是越少越好的,而这个段数在check的时候就可以求出来了。
显然你要取最小的前几个一定是最优的,这样才能保证重排后 的位置数最小。
这样就做完了。
不是很清除,凑和着看吧:
/*
* @Author: ftt2333
* @Email: ftt2333@126.com
* @Last Modified time: 2022-11-10 10:50:00
*/
#include <bits/stdc++.h>
using namespace std;
#define off ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fin(s) freopen(s, "r", stdin)
#define fout(s) freopen(s, "w", stdout)
#define fio(s) fin(s".in"), fout(s".out")
using ll = long long; using ull = uint64_t;
using lll = __int128; using ulll = __uint128_t;
using db = double; using ldb = long double;
using pii = pair<int, int>; using pll = pair<ll, ll>;
using vi = vector<int>; using vl = vector<ll>;
using uchar = unsigned char; using uint = unsigned int;
template<class T> using uid = uniform_int_distribution<T>;
template<class T> using urd = uniform_real_distribution<T>;
#define rep(i, a, b) for(auto i = (a); i <= (b); i++)
#define per(i, a, b) for(auto i = (a); i >= (b); i--)
#define go(i, h, e, x) for(int i = h[x]; i; i = e[i].nxt)
#define pb push_back
#define fi first
#define se second
#define all(a) (a).begin(), (a).end()
#define szof(a) ((int)(a).size())
#define mem(a, b) memset(a, b, sizeof(a))
#define mcpy(a, b) memcpy(a, b, sizeof(a))
mt19937 rnd(random_device{}());
const int mod = 998244353, N = 2e5 + 10;
int a[N], n, m, sum[N], id[N];
bool vis[N];
int check(int len) {
set<int> s;
rep(i, 1, len) {
if(a[i] > a[1]) continue;
auto it = s.upper_bound(a[i]);
if(it != s.begin()) s.erase(--it);
s.insert(a[i]);
}
rep(i, 1, n) sum[i] = 0;
rep(i, len + 1, n) sum[a[i]]++;
rep(i, 2, n) sum[i] += sum[i - 1];
int j = szof(s);
for(auto x: s) {
if(j + sum[x] >= m) return j;
j--;
}
return 0;
}
int main() {
off;
cin >> n >> m; rep(i, 1, n) cin >> a[i], id[a[i]] = i;
int l = 0, r = n;
for(; l < r; ) {
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
int res = m - check(l);
vi heads; heads.pb(1);
if(1 > l) res--;
rep(i, 1, n) if(res && id[i] != 1 && id[i] > l) heads.pb(id[i]), res--;
sort(all(heads), [&](int x, int y) {
return a[x] > a[y];
});
for(int x: heads) vis[x] = 1;
for(int x: heads) {
cout << a[x] << ' ';
int i = x + 1;
for(; i <= n && !vis[i]; i++) cout << a[i] << ' ';
}
}