[九省联考2018]IIIDX
【题目背景】
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
60pts的做法比较好想
就是把d按从大到小排个序
然后每到一个点i,把最大的size[i]个权值拿出来分配给它及它的子树
但是这样并不能处理d不同的情况
所以正解是一种肥肠NB的做法(我显然不会)
就是先把d按照从大到小排个序
还是求出每个点的size
然后用线段树维护tmin[i]
tmin[i]表示每个权值左边(就是比ta大的权值)还有多少能选
其实和60pts的思路有点相似
取一个点肯定就是尽量取大的
那么就使用第size大的
当取一个点的权值后,要给ta的子树占权值
所以就选和第size大的权值相同且位置最靠右的权值(从左向右满足不上升==)
这样能为ta的子树占到更大的权值
这些权值都在ta的左边(>=ta的权值)
所以就把ta的右边的tmin都减掉size
还有一点就是当更新到一个有父亲的点的时候
把ta父亲给ta占的点都加回来(但只能加一次)
#include<cstdio>
#include<cstring>
#include<algorithm>
# define ls now<<1
# define rs now<<1|1··
const int M = 500005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
double k ;
bool vis[M] ;
int n , d[M] , size[M] , fa[M] , tmin[M<<2] , tag[M<<2] , p[M] , cnt[M] ;
inline bool Cmp(int a , int b) { return a > b ; }
inline void pushup(int now){
tmin[now] = min(tmin[ls] , tmin[rs]) ;
}
void Build(int l , int r , int now) {
if(l == r) {
tmin[now] = l ;
return ;
}
int mid = (l + r)>>1 ;
Build(l , mid , ls) ; Build(mid + 1 , r , rs) ;
pushup(now) ;
}
inline void pushdown(int now) {
if(tag[now]) {
tmin[ls] += tag[now] ; tmin[rs] += tag[now] ;
tag[ls] += tag[now] ; tag[rs] += tag[now] ;
tag[now] = 0 ;
}
}
void Change(int L , int R , int val , int l , int r , int now) {
if(l > R || r < L) return ;
if(l == L && r == R) {
tmin[now] += val ;
tag[now] += val ;
return ;
}
int mid = (l + r)>>1 ;
pushdown(now) ;
if(mid >= R) Change(L , R , val , l , mid , ls) ;
else if(mid < L) Change(L , R , val , mid + 1 , r , rs) ;
else {
Change(L , mid , val , l , mid , ls) ;
Change(mid + 1 , R , val , mid + 1 , r , rs) ;
}
pushup(now) ;
}
int query(int l , int r , int x , int now) {
if(l == r) return tmin[now] >= x ? l : l + 1 ;
pushdown(now) ;
int mid = (l + r)>>1 ;
if(tmin[rs] >= x) return query(l , mid , x , ls) ;
else return query(mid + 1 , r , x , rs) ;
}
int main() {
scanf("%d%lf",&n,&k) ;
for(int i = 1 ; i <= n ; i ++) {
d[i] = read() ;
size[i] = 1 ;
fa[i] = i/k ;
}
sort(d + 1 , d + n + 1 , Cmp) ;
for(int i = n - 1 ; i >= 1 ; i --)
if(d[i] == d[i + 1]) cnt[i] = cnt[i + 1] + 1 ;
else cnt[i] = 0 ;
for(int i = n ; i >= 1 ; i --) size[fa[i]] += size[i] ;
Build(1 , n , 1) ;
for(int i = 1 ; i <= n ; i ++) {
if(fa[i] && !vis[fa[i]]) {
Change(p[fa[i]] , n , size[fa[i]] - 1 , 1 , n , 1) ;
vis[fa[i]] = 1 ;
}
int x = query(1 , n , size[i] , 1) ;
x += cnt[x] ; p[i] = x ;
Change(x , n , - size[i] , 1 , n , 1) ;
}
for(int i = 1 ; i <= n ; i ++) printf("%d ",d[p[i]]) ;
return 0 ;
}