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)?'\n':' '));
    return 0;
}
View Code

 

posted @ 2018-04-10 19:38  Ren_Ivan  阅读(180)  评论(0编辑  收藏  举报