【BZOJ5249】【九省联考2018】—IIIDX(线段树)
题意:给你一座森林和一堆权值,要把每个权值分配给点,要保证儿子不小于父亲的权值,问字典序最大的方案
开始第一眼以为是傻逼题,结果交上去果断挂掉
因为在有权值相同的情况,比如说,答案是,实际上则是
发现每个要要在保证自己最大的情况下是需要给后面的子树“预留”一些节点的,而且自己也要满足别的点的预留情况
可以用线段树维护一下第大的数能给更小的数预留多少
这样分配的时候可以在线段树上二分找到最大的满足的点
记得加了一个点后要把它父亲预留的影响去掉
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
return res*f;
}
const int N=500005;
int tr[N<<2],tag[N<<2];
int cnt[N],a[N],pos[N],fa[N],siz[N],n;
double k;
#define lc (u<<1)
#define rc ((u<<1)|1)
#define mid ((l+r)>>1)
inline void pushup(int u){
tr[u]=min(tr[lc],tr[rc]);
}
inline void pushdown(int u){
tr[lc]+=tag[u],tr[rc]+=tag[u],tag[lc]+=tag[u],tag[rc]+=tag[u],tag[u]=0;
}
void buildtree(int u,int l,int r){
if(l==r){tr[u]=l;return;}
buildtree(lc,l,mid);
buildtree(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int st,int des,int p){
if(st<=l&&r<=des){
tr[u]+=p,tag[u]+=p;return;
}
pushdown(u);
if(st<=mid)update(lc,l,mid,st,des,p);
if(mid<des)update(rc,mid+1,r,st,des,p);
pushup(u);
}
int query(int u,int l,int r,int p){
if(l==r)return tr[u]>=p?l:l+1;
pushdown(u);
if(p<=tr[rc])return query(lc,l,mid,p);
else return query(rc,mid+1,r,p);
}
inline bool comp(int a,int b){
return a>b;
}
int main(){
n=read(),scanf("%lf",&k);
for(int i=1;i<=n;i++)siz[i]=1,a[i]=read();
sort(a+1,a+n+1,comp);
for(int i=n;i;i--)
fa[i]=(int)floor((double)i/k),siz[fa[i]]+=siz[i];
cnt[n]=n;
for(int i=n-1;i;i--)
if(a[i]==a[i+1])cnt[i]=cnt[i+1];else cnt[i]=i;
buildtree(1,1,n);
for(int i=1;i<=n;i++){
if(fa[i]&&fa[i]!=fa[i-1])update(1,1,n,pos[fa[i]],n,siz[fa[i]]-1);
int p=cnt[query(1,1,n,siz[i])];
cnt[p]++,pos[i]=p;
update(1,1,n,p,n,-siz[i]);
}
for(int i=1;i<=n;i++)cout<<a[pos[i]]<<" ";
}