[九省联考2018]IIIDX

题目描述

这一天,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。那假如是你,你会怎么解决这份任务呢?
题解
如果这n个数互不相同,那么直接自底向上依次选就好了。
但是如果其中有相同的数,那么直接贪心不能保证字典序最大。
所以还是要从前往后一次考虑。
正解还是比较神仙的。
对于一个位置,要从可选集合中找出最大的x个元素作为这个元素的子树,然后把这x个位置占用一下,到访问到第一个儿子的时候把那x-1个位置再拿出来。
看起来挺简单的,写起来是越写越懵逼。
我们可以把权值从大到小排序,维护一颗线段树,每个节点维护当前位置的左边可选元素个数。
如果那个位置有重复元素,默认选最右边的。
这样可以保证当前选择方案能够最大化兄弟节点的权值。
具体按照代码理解吧。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 500002
using namespace std;
int tr[N<<2],la[N<<2],num[N],n,size[N],ans[N],a[N];
double k;
bool tag[N]; 
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline bool cmp(int a,int b){return a>b;}
inline void pushdown(int cnt){
    tr[cnt<<1]+=la[cnt];tr[cnt<<1|1]+=la[cnt];
    la[cnt<<1]+=la[cnt];la[cnt<<1|1]+=la[cnt];
    la[cnt]=0;
}
void build(int cnt,int l,int r){
    if(l==r){tr[cnt]=l;return;}
    int mid=(l+r)>>1;
    build(cnt<<1,l,mid);build(cnt<<1|1,mid+1,r);
    tr[cnt]=min(tr[cnt<<1],tr[cnt<<1|1]);
}
void upd(int cnt,int l,int r,int L,int R,int x){
    if(l>=L&&r<=R){
        tr[cnt]+=x;la[cnt]+=x;return;
    }
    int mid=(l+r)>>1;
    if(la[cnt])pushdown(cnt);
    if(mid>=L)upd(cnt<<1,l,mid,L,R,x);
    if(mid<R)upd(cnt<<1|1,mid+1,r,L,R,x);
    tr[cnt]=min(tr[cnt<<1],tr[cnt<<1|1]);
}
int _searsh(int cnt,int l,int r,int x){
    if(l==r)return tr[cnt]>=x?l:l+1;
    int mid=(l+r)>>1;
    if(la[cnt])pushdown(cnt);
    if(tr[cnt<<1|1]>=x)return _searsh(cnt<<1,l,mid,x);
    else return _searsh(cnt<<1|1,mid+1,r,x);
}
int main(){
    n=rd();scanf("%lf",&k);
    for(int i=1;i<=n;++i)a[i]=rd();
    sort(a+1,a+n+1,cmp);
    for(int i=n;i>=1;--i)num[i]=a[i]==a[i+1]?num[i+1]+1:0;
    build(1,1,n);
    for(int i=n;i>=1;--i){
        size[i]++;
        size[(int)(i/k)]+=size[i];
    }
    for(int i=1;i<=n;++i){
        int fa=int((double)i/k);
        if(fa&&!tag[fa])upd(1,1,n,ans[fa],n,size[fa]-1),tag[fa]=1;
        int x=_searsh(1,1,n,size[i]);
        x+=num[x];num[x]++;x-=(num[x]-1);
        ans[i]=x;
        upd(1,1,n,ans[i],n,-size[i]);
    }
    for(int i=1;i<=n;++i)printf("%d ",a[ans[i]]);
    return 0;
}
 
posted @ 2019-02-18 14:15  comld  阅读(357)  评论(0编辑  收藏  举报