题目描述
Osu 听过没?那是 Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在,他在世界知名游戏公司 KONMAI 内工作,离他的梦想也越来越近了。
这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻。同时,为了使玩家在游戏上~~氪更多的金钱~~花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。
这一天,Konano 接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目的解锁顺序。游戏内共有 $n$ 首曲目,每首曲目都会有一个难度 $d$,游戏内第 $i$ 首曲目会在玩家 Pass 第 $leftlfloor frac i k ight floor$ 首曲目后解锁($leftlfloor x ight floor$ 为下取整符号)若 $leftlfloor frac i k ight floor = 0$,则说明这首曲目**无需解锁**。
举个例子:当 $k = 2$ 时,第 $1$ 首曲目是无需解锁的($leftlfloor frac 12 ight floor = 0$),第 $7$ 首曲目需要玩家 Pass 第 $leftlfloor frac 72 ight floor = 3$ 首曲目才会被解锁。
Konano 的工作,便是安排这些曲目的顺序,使得每次解锁出的曲子的难度**不低于**作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个 $i$ 满足 $d_i geq d_{leftlfloor frac ik ight floor}$。
数据范围
$1 le n le 5 imes 10^5,1 le k,d le 10^9$
题解
(此处省略错误贪心)
转化成有根树,然后考虑从 $1 sim n$ 放数,将 $d$ 从大到小排序。
对于每个数 $x$ ,我们想要其最大,所以我们可以设 $f_i$ 为 $i$ 前还能放的位置,然后找到最靠前的 $i$ ,使得 $min{f_j}(j in [i,n]) ge size_x$ 即可,然后找到和 $i$ 相同的数的最后一个位置 $j$ ,将 $[j,n]$ 的位置的 $f$ 都减去 $size_x$ 即可,这些操作用线段树维护即可。
注意要把父亲的 $size$ 加回来
代码
#include <bits/stdc++.h> #define db double using namespace std; const int N=5e5+5; db k; int n,d[N],fa[N],sz[N],pr[N],a[N],in[N<<2],tg[N<<2]; #define Ls u<<1 #define Rs u<<1|1 #define mid ((l+r)>>1) void build(int u,int l,int r){ in[u]=l; if (l==r) return; build(Ls,l,mid);build(Rs,mid+1,r); } void push(int u,int v){tg[u]+=v;in[u]+=v;} void down(int u){ push(Ls,tg[u]);push(Rs,tg[u]);tg[u]=0; } void upd(int u,int l,int r,int x,int v){ if (x<=l) return push(u,v); if (tg[u]) down(u); if (mid>=x) upd(Ls,l,mid,x,v); upd(Rs,mid+1,r,x,v); in[u]=min(in[Ls],in[Rs]); } int find(int u,int l,int r,int v){ if (l==r) return l+(in[u]<v); if (tg[u]) down(u); if (in[Rs]>=v) return find(Ls,l,mid,v); return find(Rs,mid+1,r,v); } int main(){ cin>>n>>k; for (int i=1;i<=n;i++) scanf("%d",&d[i]),sz[i]=1, fa[i]=(int)((db)i/k); sort(d+1,d+n+1); reverse(d+1,d+n+1); for (int i=n;i;i--) sz[fa[i]]+=sz[i], pr[i]=d[i]==d[i+1]?pr[i+1]+1:0; build(1,1,n); for (int j,i=1;i<=n;i++){ if (fa[i] && fa[i]!=fa[i-1]) upd(1,1,n,a[fa[i]],sz[fa[i]]-1); j=find(1,1,n,sz[i]); j+=pr[j];pr[j]++;j-=(pr[j]-1); a[i]=j;upd(1,1,n,j,-sz[i]); } for (int i=1;i<=n;i++) printf("%d",d[a[i]]), putchar(i<n?' ':' '); return 0; }