ARC114F Permutation Division
题意
给定一个 排列 \(P\),将其划分成恰好 \(K\) 段,然后重排这些段得到排列 \(Q\), 使得 \(Q\) 字典序最大。
求最优的划分方案,使进行上述操作后 \(Q\) 字典序最小。
\(|P| \le 2\times 10^5\)。
题解
显然有 \(Q \ge P\)。
那么最小化字典序等价于最大化 \(P, Q\) 的最长公共前缀 (LCP) 的前提下最小化 LCP 后一个字符。
由于要划分恰好 \(K\) 段,我们要最大化 LCP 部分的段数,即求 \(P\) 一个前缀的最长下降子序列 (LDS)。
按 值从小到大 枚举 LCP 部分最后一段的开头元素 \(v\),然后尽可能延长最后一段,只要保证剩余部分有 \(K - \operatorname{LDS}\) 个元素小于 \(v\) 即可(之后段的开头元素应小于 \(v\),否则与字典序最大矛盾)。
那么只要用 BIT 维护小于 \(v\) 的所有元素,并在 BIT 上二分即可,复杂度 \(\mathcal O(n \log n)\)。
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <numeric>
#include <vector>
#include <cassert>
#include <functional>
using namespace std;
#define LOG(f...) fprintf(stderr, f)
int LOG2(int __n) { return (int)sizeof(int) * __CHAR_BIT__ - 1 - __builtin_clz(__n); }
using ll = long long;
template<class T> void read(T &x) {
char ch; x = 0;
int f = 1;
while (isspace(ch = getchar()));
if (ch == '-') ch = getchar(), f = -1;
do x = x * 10 + (ch - '0'); while(isdigit(ch = getchar()));
x *= f;
}
template<class T, class ...A> void read(T &x, A&... args) { read(x); read(args...); }
const int N = 200005;
int n, logn, K;
int a[N], b[N], f[N], cnt = 0;
int lds[N];
int tr[N];
void insert(int p) {
for (; p <= n; p += p & -p) ++tr[p];
}
int kth(int k) {
int p = 0, np;
for (int i = logn; i >= 0; --i) {
np = p | (1 << i);
if (np <= n && k > tr[np])
p = np, k -= tr[np];
}
return p + 1;
}
bool mark[N];
int pos[N];
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
read(n, K);
logn = LOG2(n);
for (int i = 1; i <= n; ++i) read(a[i]);
if (a[1] <= K) {
for (int i = 1; i <= n; ++i)
if (a[i] <= K) {
mark[i] = true;
pos[a[i]] = i;
}
for (int i = K; i >= 1; --i) {
printf("%d ", i);
for (int j = pos[i] + 1; j <= n && !mark[j]; ++j)
printf("%d ", a[j]);
}
putchar('\n');
return 0;
}
for (int i = 1; i <= n; ++i) {
if (a[i] > a[1]) {
lds[i] = lds[i - 1];
continue;
}
int p = lower_bound(f + 1, f + cnt + 1, a[i], greater<int>()) - f;
f[p] = a[i];
cnt = max(cnt, p);
lds[i] = p;
}
if (cnt >= K) {
for (int i = 1; i <= n; ++i)
printf("%d ", a[i]);
putchar('\n');
return 0;
}
for (int i = 1; i <= n; ++i) pos[a[i]] = i;
pair<int, int> res(0, 0);
for (int i = 1; i <= a[1]; ++i) {
int p = pos[i];
int rk = K - lds[p];
if (rk < i) {
int kp = kth(i - rk);
if (kp > p)
res = max(res, make_pair(kp, lds[p]));
}
insert(p);
}
for (int i = 1; i < res.first; ++i)
printf("%d ", a[i]);
int cnt = K - res.second;
partial_sort_copy(a + res.first, a + n + 1, b, b + cnt);
for (int i = 0; i < cnt; ++i)
mark[pos[b[i]]] = true;
for (int i = cnt - 1; i >= 0; --i) {
printf("%d ", b[i]);
for (int j = pos[b[i]] + 1; j <= n && !mark[j]; ++j)
printf("%d ", a[j]);
}
putchar('\n');
return 0;
}