[LibreOJ 2472][九省联考2018]IIIDX
一直没有填的坑……今天乱写了写跑过了然后厚颜无耻凑一篇题解
还有样例过于草(出题人钦点淫梦运营)
考虑从小到大枚举序列的每一位置\(p\),然后我们发现每一位置的值的合法性事单调的(小的合法,大了就不合法),这样可以对每一个位置二分答案。
然后我们考虑二分完了答案\(x\)怎么判定。把值离散化一下然后用一个线段树来维护\(T_i\)(表示当前还没有被预定的大于等于\(i\)的数有多少个),然后判定答案就看到\(x\)的前缀区间是否都不小于\(p\)所在子树的大小。同时注意顺次枚举每个点的时候要去除他对他父亲的影响,然后每次一个点确定是什么值了也要修改对应的前缀区间。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <functional>
#include <utility>
using R = long double;
const int maxn = 500005;
const int maxno = maxn << 2;
int n; R k;
int minv[maxno], addv[maxno];
void maintain(int o) {
int lc = o << 1, rc = o << 1 | 1;
minv[o] = std::min(minv[lc], minv[rc]);
}
void paint(int o, int v) {
addv[o] += v; minv[o] += v;
}
void pushdown(int o) {
if(addv[o] != 0) {
int lc = o << 1, rc = o << 1 | 1;
paint(lc, addv[o]); paint(rc, addv[o]);
addv[o] = 0;
}
}
int left[maxn];
void build_tree(int o, int L, int R) {
// addv[o] = 0;
if(L == R) {
minv[o] = left[L];
} else {
int M = (L + R) / 2;
build_tree(o << 1, L, M);
build_tree(o << 1 | 1, M + 1, R);
maintain(o);
}
}
void update(int o, int L, int R, int ql, int qr, int v) {
if(ql <= L && R <= qr) {
paint(o, v);
} else {
pushdown(o);
int M = (L + R) / 2;
if(ql <= M) update(o << 1, L, M, ql, qr, v);
if(qr > M) update(o << 1 | 1, M + 1, R, ql, qr, v);
maintain(o);
}
}
const int INF = 0x7f7f7f7f;
int query_min(int o, int L, int R, int ql, int qr) {
if(ql <= L && R <= qr) {
return minv[o];
} else {
pushdown(o);
int M = (L + R) / 2, ans = INF;
if(ql <= M) ans = std::min(ans, query_min(o << 1, L, M, ql, qr));
if(qr > M) ans = std::min(ans, query_min(o << 1 | 1, M + 1, R, ql, qr));
return ans;
}
}
int d[maxn], d2[maxn];
int lsiz;
void process() {
std::sort(d + 1, d + 1 + n);
std::sort(d2 + 1, d2 + 1 + n);
lsiz = std::unique(d2 + 1, d2 + 1 + n) - d2 - 1;
for(int i = 1; i <= n; i ++) {
d[i] = std::lower_bound(d2 + 1, d2 + 1 + lsiz, d[i]) - d2;
left[d[i]] ++;
}
for(int i = lsiz - 1; i >= 1; i --) {
left[i] += left[i + 1];
}
build_tree(1, 1, lsiz);
}
int A[maxn], siz[maxn];
void solve() {
for(int i = n; i >= 1; i --) {
siz[i] ++;
int f = (int)floor((R(i)) / k);
siz[f] += siz[i];
}
for(int i = 1; i <= n; i ++) {
int f = (int)floor((R(i)) / k);
if(f > 0) {
update(1, 1, lsiz, 1, A[f], siz[i]);
}
int L = (f == 0) ? 1 : A[f], R = lsiz, ret;
while(true) {
if(R - L <= 3) {
for(int j = R; j >= L; j --) {
if(query_min(1, 1, lsiz, 1, j) >= siz[i]) {
ret = j; break;
}
}
break;
}
int M = L + (R - L) / 2;
if(query_min(1, 1, lsiz, 1, M) >= siz[i]) {
L = M;
} else {
R = M;
}
}
A[i] = ret; update(1, 1, lsiz, 1, ret, -siz[i]);
}
}
int main() {
scanf("%d%Lf", &n, &k);
for(int i = 1; i <= n; i ++) {
scanf("%d", &d[i]); d2[i] = d[i];
}
process(); solve();
for(int i = 1; i <= n; i ++) {
if(i > 1) putchar(' ');
printf("%d", d2[A[i]]);
}
puts(""); return 0;
}