题目背景
Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了。
这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。
题解
-
先从大到小排序,每次找一个最靠左的位置,使得这个位置左边足够放下当前节点的子树
-
若有多个值相同,应该放到最右边那个值那里
-
显然满足单调性,毕竟如果某个位置可以满足,那么该位置右边的位置也一定能够满足
-
nxt 数组用来求同一个值的最右边在哪个位置。更新的时候向左移一个,防止取到相同的位置
-
线段树修改的时候,最小值直接加上修改的值就行了,不用乘上区间长度,更新儿子节点的时候,要把他的父亲给他预留的位置先去掉
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #define N 500010 6 using namespace std; 7 double k; 8 int n,a[N],f[N],ans[N],size[N],fa[N],nxt[N],mn[N<<2],tag[N<<2]; 9 void build(int d,int l,int r) 10 { 11 if (l==r) { mn[d]=l; return;} 12 int mid=l+r>>1; 13 build(d*2,l,mid),build(d*2+1,mid+1,r); 14 mn[d]=min(mn[d*2],mn[d*2+1]); 15 } 16 void push(int d) 17 { 18 if (!tag[d]) return; 19 mn[d*2]+=tag[d],mn[d*2+1]+=tag[d],tag[d*2]+=tag[d],tag[d*2+1]+=tag[d],tag[d]=0; 20 } 21 void change(int d,int L,int R,int x,int l=1,int r=n) 22 { 23 if (L<=l&&r<=R) { tag[d]+=x,mn[d]+=x; return; } 24 int mid=l+r>>1; push(d); 25 if (L<=mid) change(d*2,L,R,x,l,mid); 26 if (R>mid) change(d*2+1,L,R,x,mid+1,r); 27 mn[d]=min(mn[d*2],mn[d*2+1]); 28 } 29 int find(int d,int x,int l=1,int r=n) 30 { 31 if (l==r) return l+(mn[d]<x); 32 int mid=l+r>>1; push(d); 33 return x<=mn[d*2+1]?find(d*2,x,l,mid):find(d*2+1,x,mid+1,r); 34 } 35 int main() 36 { 37 scanf("%d%lf",&n,&k); 38 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 39 sort(a+1,a+n+1),reverse(a+1,a+n+1); 40 for (int i=n-1;i;i--) if (a[i]==a[i+1]) nxt[i]=nxt[i+1]+1; 41 for (int i=1;i<=n;i++) fa[i]=i/k,size[i]=1; 42 for (int i=n;i;i--) size[fa[i]]+=size[i]; 43 build(1,1,n); 44 for (int i=1;i<=n;i++) 45 { 46 if (fa[i]!=fa[i-1]) change(1,ans[fa[i]],n,size[fa[i]]-1); 47 int p=find(1,size[i]); 48 p+=nxt[p];nxt[p]++;p-=nxt[p]-1;ans[i]=p,change(1,p,n,-size[i]); 49 } 50 for (int i=1;i<=n;i++) printf("%d ",a[ans[i]]); 51 }