发现可以建出一棵树,给树分配权值,祖先比子孙的权值小,且让整棵树按标号输出权值最大
有一个比较容易想到的贪心 ----
考虑一个点 u,它的大小 为 siz[u],我们每次从能够选择的权值中挑 siz[u] 个最大的,并将最后一个赋给 u
这样做在权值没有重复的时候是对的,但有重复就会 GG
因为兄弟可以跟它的子树里面的点换,仍然满足题意,但兄弟变大,也就是答案更优
举个例子,权值为 9 9 8 7 6 6 6 6 5 5
siz[2] 为 7,那么2预留 9 9 8 7 6 6 ,然后 2 选 6 ?
不,这样不是最优,因为我们可以将 3(本身是6),与2子树中的9交换,这样 2及它的子树的值为, 6 6 6 6 7 8 9
仍然满足题意,还使 3 达到最大
我们发现可以通过线段树来完成这个预留操作
定义f[i]数组表示i号点左边(包括它自己)最多可以再放多少个点
那么f[i]数组一定是一个单调不下降的数组,我们可以用线段树来维护 f 数组和某段区间的 f 最小值
那么我们就可以每次在线段树上二分出那个最左边的位置
再通过一个cnt 数组来存储当前点向右位移几个距离能够到达使当前点最优并且能给同层的点提供一个可能更优的位置的位置
就可以解决这个问题了
#include<bits/stdc++.h>
#define N 500050
using namespace std;
int Min[N<<2], tag[N<<2];
int n, a[N], b[N], cnt[N], ans[N]; double k;
bool cmp(int x, int y){ return x > y;}
int fa[N], siz[N], vis[N];
void Pushup(int x){ Min[x] = min(Min[x<<1], Min[x<<1|1]);}
void Build(int x, int l, int r){
tag[x] = 0; if(l == r){ Min[x] = l; return;}
int mid = (l+r) >> 1;
Build(x<<1, l, mid); Build(x<<1|1, mid+1, r);
Pushup(x);
}
void Pushnow(int x, int v){ Min[x] += v; tag[x] += v;}
void Pushdown(int x){
if(tag[x] == 0) return;
Pushnow(x<<1, tag[x]); Pushnow(x<<1|1, tag[x]);
tag[x] = 0;
}
void Modify(int x, int l, int r, int L, int R, int v){
// cout<<L<<" "<<R<<" "<<v<<endl;
if(L<=l && r<=R){ Pushnow(x, v); return;}
Pushdown(x); int mid = (l+r) >> 1;
if(L<=mid) Modify(x<<1, l, mid, L, R, v);
if(R>mid) Modify(x<<1|1, mid+1, r, L, R, v);
Pushup(x);
}
int Find(int x, int l, int r, int k){
if(l == r){
if(Min[x] < k) return l+1;
else return l;
}
Pushdown(x); int mid = (l+r) >> 1;
if(Min[x<<1|1] >= k) return Find(x<<1, l, mid, k);
else return Find(x<<1|1, mid+1, r, k);
}
int main(){
//freopen("1.in","r",stdin);
scanf("%d%lf", &n, &k);
for(int i=1; i<=n; i++) scanf("%d", &a[i]), a[i];
sort(a+1, a+n+1, cmp);
for(int i=n-1; i>=1; i--){
cnt[i] = (a[i] == a[i+1]) ? cnt[i+1] + 1 : 0;
} Build(1, 1, n);
for(int i=n; i>=1; i--){
siz[i] += 1; int f = i / k;
fa[i] = f; siz[f] += siz[i];
}
for(int i=1; i<=n; i++){
if(fa[i] && !vis[fa[i]]){
Modify(1, 1, n, ans[fa[i]], n, siz[fa[i]]-1);
vis[fa[i]] = 1;
}
int pos = Find(1, 1, n, siz[i]); pos += cnt[pos];
cnt[pos]++; pos -= (cnt[pos]-1);
Modify(1, 1, n, pos, n, -siz[i]);
ans[i] = pos;
}
for(int i=1; i<=n; i++) printf("%d ", a[ans[i]]);
return 0;
}