【题目链接】
【思路要点】
- 按照题意,依次先确定每个节点可以填的最大的数。
- 在某个位置\(i\)填入一个数\(x\)时,需要保证可以预定\(Size_{i}-1\)个大于等于\(x\)的数到\(i\)的子树中。
- 如何判断是否能够预定?这实际上就是判断一个二分图是否存在完美匹配,Hall定理即可。
- 具体实现时需要用一棵支持区间加减,维护区间最小值线段树完成。
- 时间复杂度\(O(NLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5e5 + 5; const double eps = 1e-10; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct SegmentTree { struct Node { int lc, rc; int tag, Min; } a[MAXN * 2]; int n, size, root, val[MAXN], nxt[MAXN]; void update(int root) { a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min); } void build(int &root, int l, int r) { root = ++size; if (l == r) { a[root].Min = l; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; build(root, 1, n); for (int i = n; i >= 1; i--) if (val[i] != val[i + 1]) nxt[i] = i; else nxt[i] = nxt[i + 1]; } void pushdown(int root) { int tmp = a[root].tag; if (tmp == 0) return; a[a[root].lc].tag += tmp; a[a[root].lc].Min += tmp; a[a[root].rc].tag += tmp; a[a[root].rc].Min += tmp; a[root].tag = 0; } void modify(int root, int l, int r, int ql, int qr, int d) { if (l == ql && r == qr) { a[root].Min += d; a[root].tag += d; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) modify(a[root].lc, l, mid, ql, min(qr, mid), d); if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d); update(root); } void modify(int x, int d) { modify(root, 1, n, x, n, d); } int query(int root, int l, int r, int val) { if (a[root].Min >= val) return l; else if (l == r) return r + 1; pushdown(root); int mid = (l + r) / 2; if (a[a[root].rc].Min >= val) return query(a[root].lc, l, mid, val); else return query(a[root].rc, mid + 1, r, val); } int query(int size) { int pos = query(root, 1, n, size); pos = nxt[pos]; modify(pos, -size); return pos; } } ST; bool vis[MAXN]; double k; int n, nxt[MAXN], ans[MAXN]; int father[MAXN], size[MAXN]; bool cmp(int x, int y) {return x > y; } int main() { read(n); scanf("%lf", &k); for (int i = 1; i <= n; i++) read(ST.val[i]); for (int i = 1; i <= n; i++) { father[i] = (int) (i / k + eps); size[i] = 1; } for (int i = n; i >= 1; i--) size[father[i]] += size[i]; sort(ST.val + 1, ST.val + n + 1, cmp); ST.init(n); for (int i = 1; i <= n; i++) { if (father[i] != father[i - 1] && father[i] != 0) ST.modify(ans[father[i]], size[father[i]] - 1); ans[i] = ST.query(size[i]); } for (int i = 1; i <= n; i++) printf("%d ", ST.val[ans[i]]); return 0; }