BZOJ_5249_[2018多省省队联测]_IIIDX_线段树
BZOJ_5249_[2018多省省队联测]IIIDX_线段树
Description
【题目背景】
Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了。这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。
【题目描述】
这一天,Konano接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目的解锁顺序。游戏内共有n首曲目,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第⌊i/k⌋首曲目后解锁(x为下取整符号)若⌊i/k⌋=0,则说明这首曲目无需解锁。举个例子:当k=2时,第1首曲目是无需解锁的(⌊1/2⌋=0),第7首曲目需要玩家Pass第⌊7/2⌋=3首曲目才会被解锁。Konano的工作,便是安排这些曲目的顺序,使得每次解锁出的曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i满足Di≥⌊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
114 514 1919 810
Sample Output
114 810 514 1919
首先可以发现题目给出的关系是一棵森林,而我们要给森林中的每个节点权值,使得每个节点的权值都要小于等于子树内任何一点的权值,并且使权值字典序最大。
建一个虚点向所有i<k的点连边,于是森林就成了一棵树,并处理出子树的大小siz。
把权值从大到小排序,对于每个点,我要选当前剩下的权值里第siz大的,然后给子树预定这个点前面的权值,递归处理。
这是一种错误的贪心,错误数据
4 2.0
1 1 1 2
答案是1 1 2 1而错误的贪心输出1 1 1 2。
当权值各不相同的时候这个贪心是对的,因为第siz大的就是我们要的,子树用比它大的那siz−1个即可。
而如果权值有重复的,我们要找和第siz大的权值相等的权值中最靠右的那个点,再给子树预定siz−1个,这样做能够让当前点权值尽可能大的前提下给子树预定的权值尽可能的小,这样,万一下一个要分配的点不在这个点的子树中,也能被分配尽可能大的权值。
怎么操作呢?
设F[i]为第i个权值左边有多少个没被预定,即可用数量。初始值F[i]=i。我们在线段树上维护F[i]和F[i]的最大值。
举个官方题解中的例子:9 8 7 6 5 5 5 5 5 4 3 2
假如第一个结点的子树大小为7,那么第一个结点的权值就应该是第7大的数。
初始时F[i]=i,我们发现F[12]>F[11]>F[10]>F[9]>F[8]>F[7]=7,即第7个点右侧的可用数量都大于7,我们刚好应该选择第7个数5,同时让被选的5的位置尽可能的靠右,于是选到了第9的位置。(具体做法:每个相等的值有一个cnt值,当前位置x加上cnt[x]刚好是权值为a[x]的数中最右的位置,再处理出这个位置上的数已经被拿了多少次,从x中减掉)。
接着第一个节点要给它的子树预定siz−1个位置,但我们不好确定是哪几个位置,于是F[9]∼F[n]都减去(siz−1)即可。
对于其他结点i重复上述操作:
1.找到一个位置尽可能靠前的x,满足x右边的所有F[]都大于等于siz[i]。这步查询在线段树上二分即可。
2.找到权值为a[x]中剩下的最右的位置y满足a[y]=a[x],并把a[y]分配给这个点。
3.F[y]∼F[n]减去(siz−1)
注意处理到某个点,如果这个点的父亲的预定区间没有被撤销,需要撤销。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> using namespace std; #define N 500050 #define ls p<<1 #define rs p<<1|1 typedef double du; int fa[N],siz[N],n,cnt[N],a[N],t[N<<2],del[N<<2],ans[N]; du k; bool used[N]; bool cmp( int x, int y){ return x>y; } void build( int l, int r, int p) { if (l==r) { t[p]=l; return ; } int mid=(l+r)>>1; build(l,mid,ls); build(mid+1,r,rs); t[p]=min(t[ls],t[rs]); } void pushdown( int p) { if (del[p]) { del[ls]+=del[p]; t[ls]+=del[p]; del[rs]+=del[p]; t[rs]+=del[p]; del[p]=0; } } void update( int l, int r, int x, int y, int v, int p) { if (x<=l&&y>=r) { t[p]+=v; del[p]+=v; return ; } pushdown(p); int mid=(l+r)>>1; if (x<=mid) update(l,mid,x,y,v,ls); if (y>mid) update(mid+1,r,x,y,v,rs); t[p]=min(t[ls],t[rs]); } int query( int l, int r, int x, int p) { if (l==r) return t[p]>=x?l:l+1; pushdown(p); int mid=(l+r)>>1; if (t[rs]>=x) return query(l,mid,x,ls); else return query(mid+1,r,x,rs); } int main() { scanf ( "%d%lf" ,&n,&k); int i; for (i=1;i<=n;i++) scanf ( "%d" ,&a[i]),siz[i]=1; sort(a+1,a+n+1,cmp); for (i=n-1;i>=1;i--) { if (a[i]==a[i+1]) cnt[i]=cnt[i+1]+1; else cnt[i]=0; } for (i=1;i<=n;i++) { fa[i]=( int ) floor (i/k); } for (i=n;i;i--) siz[fa[i]]+=siz[i]; build(1,n,1); for (i=1;i<=n;i++) { if (fa[i]&&!used[fa[i]]) { update(1,n,ans[fa[i]],n,siz[fa[i]]-1,1); used[fa[i]]=1; } int x=query(1,n,siz[i],1); x+=cnt[x]; cnt[x]++; x-=(cnt[x]-1); ans[i]=x; update(1,n,x,n,-siz[i],1); } int flg=0; for (i=1;i<=n;i++) { if (!flg) flg= printf ( "%d" ,a[ans[i]]); else printf ( " %d" ,a[ans[i]]); } } |
标签:
线段树
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· Linux系列:如何调试 malloc 的底层源码
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· 对象命名为何需要避免'-er'和'-or'后缀
· JDK 24 发布,新特性解读!
· Java24你发任你发,我用Java8
· .NET Core奇技淫巧之WinForm使用Python.NET并打包
· C# 中比较实用的关键字,基础高频面试题!