[九省联考2018]IIIDX——贪心

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。那假如是你,你会怎么解决这份任务呢。

思路:

很神奇的贪心思路。不难看出这是一棵树,而我们要求维护一个类似于小根堆的东西同时还要保证字典序最大,于是可以从第一个依次地贪心考虑,这样可以满足字典序最大的要求。但是我们必须要使得我们的选择是合法的,也就是如果我们选的这个点是作为了某颗子树的根,那么在比这个点要大(或等于)的数字里面必须要满足至少有size[u]个可以供选择的数(有很多满足条件的则选择最大的,有重复的数则选择最靠从大到小排序之后最靠右边的)。
我们把di的值从大到小排序,那么第i个数可以作为节点u的值当且仅当在i左边的数中可供选择的数要大于size[u],我们记Fi表示第i个数前面可供选择的数有多少个,即满足Fi>size[u]的数才可选,但是由于会有重复的数,以至于我们不知道这棵子树中到底会选择哪几个节点,所以在u前面的数都有可能被选到,所以目前我们只可以把u以及u后面的数的Fi全部都减去一个size[u](因为后面的数中前面可供选择的数的个数是确定的,不确定的只是前面的数而已),这样的话,统计可被选择的个数时就要看后面所有的数的可供选则的最小值了(因为这个点有可能自己本身就处在了一个不确定的区间中)。

做法:

用线段树维护每个点的Fi就好了,如果这个点有父亲的话,注意要加上父亲后面可供选择的值(因为之前减去是因为要给子树留位置,不让其他的点占了,而现在开始选择子树中的点了),查找的话线段树上二分就可了。

/*============================
 * Author : Ylsoi
 * Problem : iiidx
 * Algorithm : tanxin
 * Time : 2018.4.12
 * ==========================*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
void File(){
    freopen("[bzoj5249]iiidx.in","r",stdin);
    freopen("[bzoj5249]iiidx.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=5e5+10;
struct Segmen_Tree{
#define lc (rt<<1)
#define rc ((rt<<1)|1)
#define mid ((l+r)>>1)
#define lson lc,l,mid
#define rson rc,mid+1,r
    int Min[maxn<<2],tag[maxn<<2];
    void pushup(int rt){Min[rt]=Min[lc]<Min[rc] ? Min[lc] : Min[rc];}
    void pushdown(int rt){
        Min[lc]+=tag[rt];
        Min[rc]+=tag[rt];
        tag[lc]+=tag[rt];
        tag[rc]+=tag[rt];
        tag[rt]=0;
    }
    void build(int rt,int l,int r){
        if(l==r){Min[rt]=l;return;}
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update(int rt,int l,int r,int L,int R,int x){
        if(L<=l && r<=R){
            Min[rt]+=x;
            tag[rt]+=x;
        }
        else{
            pushdown(rt);
            if(L<=mid)update(lson,L,R,x);
            if(R>=mid+1)update(rson,L,R,x);
            pushup(rt);
        }
    }
    int query(int rt,int l,int r,int x){
        if(l==r)return Min[rt]>=x ? l : l+1;
        pushdown(rt);
        if(Min[rc]>=x)return query(lson,x);
        else return query(rson,x);
    }
}T;
int n,d[maxn],cnt,beg[maxn],be[maxn],pos[maxn],cnt_be,size[maxn],ans[maxn];
double k;
bool cmp(int x,int y){return x>y;}
struct edge{
    int to;
    int last;
}E[maxn];
void add(int u,int v){
    ++cnt;
    E[cnt].to=v;
    E[cnt].last=beg[u];
    beg[u]=cnt;
}
void Get_Size(int u){
    size[u]=1;
    MREP(i,u){
        int v=E[i].to;
        Get_Size(v);
        size[u]+=size[v];
    }
}
int main(){
    File();
    scanf("%d%lf",&n,&k);
    REP(i,1,n)scanf("%d",&d[i]);
    sort(d+1,d+n+1,cmp);
    REP(i,1,n+1)
        if(d[i]!=d[i-1])pos[cnt_be]=i-1,be[i]=++cnt_be;
        else be[i]=cnt_be;
    DREP(i,n,1)add(floor(i/k),i);
    Get_Size(0);
    T.build(1,1,n);
    REP(i,1,n){
        int f=floor(i/k);
        if(f)T.update(1,1,n,ans[f],n,size[i]);
        int p=T.query(1,1,n,size[i]);
        p=pos[be[p]];--pos[be[p]];
        T.update(1,1,n,p,n,-size[i]);
        ans[i]=p;
    }
    REP(i,1,n)printf("%d ",d[ans[i]]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值