[线段树] Luogu P4346 IIIDX

题目背景

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 }

 

上一篇:bzoj 4883 [Lydsy1705月赛]棋盘上的守卫 题解(思维,建图,最小基环森林)


下一篇:C++11的for循环,以及范围Range类的实现