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;
}
posted @ 2021-03-21 10:55  RiverHamster  阅读(29)  评论(0编辑  收藏  举报
\