bzoj5249 [2018多省省队联测]IIIDX

介绍了一款音乐游戏的曲目解锁顺序算法,通过构建森林结构并采用后序遍历方式来确保每次解锁的曲目难度符合游戏设定的要求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/archives/3012
Description

【题目背景】
Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在
,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了。这款音乐游戏内一般都包含了许多歌曲,歌曲
越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲
目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。
【题目描述】
这一天,Konano接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目的解锁顺序。游戏内共有n首曲目
,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第trunc(i/k)首曲目后解锁(x为下取整符号)若tru
nc(i/k)=0,则说明这首曲目无需解锁。举个例子:当k=2时,第1首曲目是无需解锁的(trunc(1/2)=0),第7首曲
目需要玩家Pass第trunc(7/2)=3首曲目才会被解锁。Konano的工作,便是安排这些曲目的顺序,使得每次解锁出的
曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i满足Di≥Dtrun
c(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
考虑将所有数都按照要求向前连接 那么显然我们可以发现这个构成了一个森林 有个显然的想法是按照这个树的顺序从前往后 然后再针对每个树都采用后序遍历的方法给数 这样的话在权值不相同的情况下显然是对的 但是在权值相同的情况下就会有问题 比如1112
采用贪心会变成1112 正解:1121
这样做的情况会有哪种情况不对 对于字符串字典序的最大我们始终需要回归本身的定义也就是从前往后一位一位贪心取最大 有可能比如我x这个子树我只需要为他选这么多个作为预留的 保证我后面有足够多的让我满足子树内的数值都大于当前节点的数值至于最后的答案是多少我按位做到那一位的时候在选择所以 首先将权值从大到小排序 然后建线段树每一位上储存这个位置左边还有多少是可用的 然后每次要填数的时候在线段树上二分一下找到第一个位置可用的大于等于size的地方 如果有相同的就取最右边的 因为这样可以让我的子树选择更多可能使得非子树部分答案更大 子树深度更大的部分答案更小 显然答案更优 注意如果做当前节点的时候发现自己是有父亲的需要将父节点预留的位置去掉 因为父节点就是预留给自己的所以去掉之后再找答案最优 最后可以手玩一下官方题解中的例子9 8 7 6 5 5 5 5 5 4 3 2

#include<cstdio>
#include<cctype>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define eps 1e-8
using namespace std;
const int N=500050;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct node{int mn,tag;}tree[N<<2];
inline void update(int x){tree[x].mn=min(tree[lc].mn,tree[rc].mn);}
inline void build(int x,int l,int r){
    if (l==r) {tree[x].mn=l;return;}int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);update(x);
}
inline void pushdown(int x){
    if (!tree[x].tag) return;static int tag;
    tag=tree[x].tag;tree[x].tag=0;
    tree[lc].mn+=tag;tree[lc].tag+=tag;
    tree[rc].mn+=tag;tree[rc].tag+=tag;
}
inline void modify(int x,int l,int r,int l1,int r1,int v){
    if (l1<=l&&r1>=r){tree[x].mn+=v;tree[x].tag+=v;return;}
    int mid=l+r>>1;pushdown(x);
    if (l1<=mid) modify(lc,l,mid,l1,r1,v);
    if (r1>mid) modify(rc,mid+1,r,l1,r1,v);update(x);
}
inline int query(int x,int l,int r,int p){
    if (l==r) return tree[x].mn>=p?l:l+1;int mid=l+r>>1;pushdown(x);
    if(p<=tree[rc].mn) return query(lc,l,mid,p);else return query(rc,mid+1,r,p);
}
inline bool cmp(const int &a,const int &b){return a>b;}
int n,d[N],size[N],cnt[N],fa[N],ans[N];double k; 
int main(){
    freopen("bzoj5249.in","r",stdin);
    n=read();scanf("%lf",&k);
    for (int i=n;i;--i) d[i]=read(),fa[i]=i/k+eps,++size[i],size[fa[i]]+=size[i];
    sort(d+1,d+n+1,cmp);build(1,1,n);
    for (int i=n-1;i;--i) if (d[i]==d[i+1]) cnt[i]=cnt[i+1]+1;
    for (int i=1;i<=n;++i){
        if(fa[i]&&fa[i-1]!=fa[i]) modify(1,1,n,ans[fa[i]],n,size[fa[i]]-1);
        int x=query(1,1,n,size[i]);
        x+=cnt[x];++cnt[x];x-=(cnt[x]-1);ans[i]=x;modify(1,1,n,x,n,-size[i]);
    }for (int i=1;i<=n;++i) printf("%d ",d[ans[i]]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值