zoukankan      html  css  js  c++  java
  • [九省联考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;
    }
     
  • 相关阅读:
    一个int类型究竟占多少个字节
    TCMalloc 安装与使用
    [创意标题] spoj 11354 Amusing numbers
    如何更好地理解和使用Github
    一天JavaScript示例-点击图片显示大图片添加鼠标
    php方法综述除去换行符(PHP_EOL使用变量)
    使用jQuery和css3实现了仿淘宝ued博客左边的菜单切换动画
    【软件使用技巧】PL/SQL Developer实现双击table询
    newlisp 接受jenkins带空格的参数
    Oracle listener lsnrctl
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10395369.html
Copyright © 2011-2022 走看看