title
BZOJ 5249
LUOGU 4364
Description
【题目背景】
Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了。这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。
【题目描述】
这一天,Konano接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目的解锁顺序。游戏内共有n首曲目,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第trunc(i/k)首曲目后解锁(x为下取整符号)若trunc(i/k)=0,则说明这首曲目无需解锁。举个例子:当k=2时,第1首曲目是无需解锁的(trunc(1/2)=0),第7首曲目需要玩家Pass第trunc(7/2)=3首曲目才会被解锁。Konano的工作,便是安排这些曲目的顺序,使得每次解锁出的曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i满足Di≥Dtrunc(i/k)。当然这难不倒曾经在信息学竞赛摸鱼许久的Konano。那假如是你,你会怎么解决这份任务呢
Input
第1行1个正整数n和1个小数k,n表示曲目数量,k其含义如题所示。
第2行n个用空格隔开的正整数d,表示这n首曲目的难度。
1 ≤ n ≤ 500000
1 < k ≤ 10^9
1 < d ≤ 10^9
Output
输出1行n个整数,按顺序输出安排完曲目顺序后第i首曲目的难度。
若有多解,则输出d1最大的;若仍有多解,则输出d2最大的,以此类推。
Sample Input
4 2.0
114 514 1919 810
Sample Output
114 810 514 1919
analysis
显然题意可以抽象成:一棵树,要对树上的每个点标上给定的权值,满足每个点上的权值都 子树内点的权值,并使这个棵树编号从小到大的权值字典序最大。
首先可以一眼得到一个做法,将权值从大到小排序,把长度为子树大小的一段按子树编号从小到大丢给它们,递归下去得到答案。
这种做法在 不重复的时候是正确的,那 要是有相同的数呢?
有可能可以将编号 子树里一个大的权值与编号 的子树根的权值替换,使得 的权值依然是能取得的最大数且子树内的数都比 的权值大,同时 子树内的点也还能标满 权值的权值。
那怎么做呢?先把给定权值从大到小排序,线段树上维护每个权值左边(包括本身)还能取的权值的个数
。
当取好一个点
的权值时,需要给它子树内的点预留取的权值,这些权值显然在
取得权值的左边,但是我们并不知道取的是哪些权值,所以只把
取得的权值右边(包括本身)的
减去
子树大小
,每次取一个点yy的权值时,只需要在线段树上找到最大权值val满足val所在位置右边(包括本身)的所有
,且val尽可能靠右即可,这个在线段树上二分就能做到。
如果一个点有父亲,求它的权值时要把它父亲为子树预留的大小去掉,需要注意的是,每个父亲预留的大小只要去掉一次,写的时候容易忽略这一点。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
struct tree
{
int val,atag;
}tree[maxn<<2];
inline void pushdown(int now)
{
tree[now<<1].val+=tree[now].atag;
tree[now<<1|1].val+=tree[now].atag;
tree[now<<1].atag+=tree[now].atag;
tree[now<<1|1].atag+=tree[now].atag;
tree[now].atag=0;
}
inline void build(int now,int l,int r)
{
if (l==r)
{
tree[now].val=l;
return ;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
tree[now].val=min(tree[now<<1].val,tree[now<<1|1].val);
}
inline void change(int now,int l,int r,int tl,int tr,int k)
{
if (tl<=l && tr>=r)
{
tree[now].val+=k;
tree[now].atag+=k;
return ;
}
pushdown(now);
int mid=(l+r)>>1;
if (tl<=mid) change(now<<1,l,mid,tl,tr,k);
if (tr>mid) change(now<<1|1,mid+1,r,tl,tr,k);
tree[now].val=min(tree[now<<1].val,tree[now<<1|1].val);
}
inline int ask(int now,int l,int r,int k)
{
if (l==r)
{
return (tree[now].val>=k?l:l+1);
}
pushdown(now);
int mid=(l+r)>>1;
if (k<=tree[now<<1|1].val) return ask(now<<1,l,mid,k);
else return ask(now<<1|1,mid+1,r,k);
}
int d[maxn];
inline bool cmp(int a,int b)
{
return a>b;
}
int cnt[maxn],fa[maxn];
int siz[maxn],ans[maxn];
int main()
{
int n;read(n);
double k;scanf("%lf",&k);
for (int i=1; i<=n; ++i)
read(d[i]);
sort(d+1,d+n+1,cmp);
for (int i=n-1; i; --i)
if (d[i]==d[i+1])
cnt[i]=cnt[i+1]+1;
else
cnt[i]=0;
for (int i=1; i<=n; ++i)
fa[i]=(int)floor(i/k),siz[i]=1;
for (int i=n; i; --i)
siz[fa[i]]+=siz[i];
build(1,1,n);
for (int i=1; i<=n; ++i)
{
if (fa[i] && fa[i]!=fa[i-1])
change(1,1,n,ans[fa[i]],n,siz[fa[i]]-1);
int x=ask(1,1,n,siz[i]);
x+=cnt[x];
++cnt[x];
x-=(cnt[x]-1);
ans[i]=x;
change(1,1,n,x,n,-siz[i]);
}
for (int i=1; i<=n; ++i)
printf("%d ",d[ans[i]]);
return 0;
}