• bzoj5249 [2018多省省队联测]IIIDX


    转化一下问题变成给定一棵树,一个序列,求父亲的权值小于子树的最大方案。

    直接贪心会在有重复权值时出现错误,我们考虑用线段树优化贪心。

    将序列从小到大排序,线段树上每个点记录他和他右边当前还可用的权值,注意这里我们并不一定维护的都是正确的,但是我们要保证我们需要用到的限制一定都体现了出来,比如 1 2 5 5 5 5 5 6 7 8 9 K=2,我们放第二个点时,会放到3这个位置,于是我们将1~3区间减7,这时候4~11的权值我们并没有修改,但是需要用到的限制只有前三个点,所以是正确的,之后我们每次在线段树上找到最大的可用位置,然后放到和他权值相同的最左边就可以了。

    还要注意我们放一个点时需要先将其父亲的限制去掉。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <map>
    #define N 500500
    using namespace std;
    int n,a[N],fa[N],size[N],pp[N];
    double K;
    int lazy[N<<2],minn[N<<2];
    void pushdown(int rt){
        if(lazy[rt]){
            lazy[rt<<1]+=lazy[rt];minn[rt<<1]+=lazy[rt];
            lazy[rt<<1|1]+=lazy[rt];minn[rt<<1|1]+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void build(int rt,int l,int r){
        if(l==r){minn[rt]=n-l+1;return;}
        int mid=(l+r)>>1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        minn[rt]=min(minn[rt<<1],minn[rt<<1|1]);
    }
    void update(int rt,int l,int r,int x,int y,int z){
        if(x<=l&&r<=y){
            lazy[rt]+=z;minn[rt]+=z;
            return ;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
        if(x<=mid)update(rt<<1,l,mid,x,y,z);
        if(y>mid)update(rt<<1|1,mid+1,r,x,y,z);
        minn[rt]=min(minn[rt<<1],minn[rt<<1|1]);
    }
    int query(int rt,int l,int r,int x){
        if(l==r){
            if(minn[rt]>=x)return l;
            return l-1;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
        if(minn[rt<<1]>=x)return query(rt<<1|1,mid+1,r,x);
        else return query(rt<<1,l,mid,x);
    }
    map<int,int> L;
    int main(){
        scanf("%d%lf",&n,&K);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++)
            if(a[i]!=a[i-1])L[a[i]]=i;
        for(int i=1;i<=n;i++)fa[i]=floor(i/K);
        for(int i=n;i;i--){
            size[i]++;
            size[fa[i]]+=size[i];
        }
        build(1,1,n);
        for(int i=1;i<=n;){
            if(fa[i])update(1,1,n,1,pp[fa[i]],size[fa[i]]-1);
            do{
                int x=query(1,1,n,size[i]);
                pp[i]=L[a[x]]++;
                update(1,1,n,1,pp[i],-size[i]);
                i++;
            }while(i<=n&&fa[i]==fa[i-1]);
        }
        for(int i=1;i<=n;i++)printf("%d%c",a[pp[i]],((i==n)?'
    ':' '));
        return 0;
    }
    View Code
  • 相关阅读:
    Reverse Words in a String II -- LeetCode
    计算两点间的距离,hdu-2001
    A + B Problem,hdu-1000
    ASCII码排序,hdu-2000
    定义#define
    break语句的使用
    判断一个数是否为素数
    用下面公式求π:
    Sum Problem-hdu-1001
    正三角形的外接圆面积,nyoj-274
  • 原文地址:https://www.cnblogs.com/Ren-Ivan/p/8781953.html
Copyright © 2020-2023  润新知