Description
Input
Output
Sample Input
4 2.0
114 514 1919 810
Sample Output
114 810 514 1919
Data Constraint
Solution
先考虑一个贪心,将数从大到小填到树的后序遍历上。
但是这只能过 di 互不相同的数据点,如数据:
4 2
1 1 1 2
就能卡的你痛不欲生……
关键在于没有预留足够的点填到一个点的子树内。
我们先将数从小到大排序,从小到大地填。
注意可以新建一个 0 号节点,把森林变成一颗树方便处理。
设一个点在线段树的后缀子树大小为
Size ,一个数的重复个数为 P ,那么这个数要填到一个点
i 满足: Sizei≥P (感性理解)。结合上述数据可以清楚地得出这个结论……
于是用线段树维护区间和还有单点修改即可。
时间复杂度为 O(N log N) 。
Code
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
using namespace std;
const int N=5e5+5;
int n,tot,qx,qy;
double k;
int first[N],nex[N],en[N],size[N];
int a[N],fa[N],ans[N],f[N<<2];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline void insert(int x,int y)
{
nex[++tot]=first[x];
first[x]=tot;
en[tot]=y;
size[x]+=size[y];
}
void change(int v,int l,int r)
{
f[v]+=qy;
if(l==r) return;
int mid=l+r>>1;
if(qx<=mid) change(v<<1,l,mid); else change(v<<1|1,mid+1,r);
}
int find(int v,int l,int r,int x)
{
if(l==r) return l;
int mid=l+r>>1;
if(f[v<<1|1]>=x) return find(v<<1|1,mid+1,r,x);
return find(v<<1,l,mid,x-f[v<<1|1]);
}
int main()
{
n=read(),scanf("%lf",&k);
for(int i=1;i<=n;i++) a[i]=read(),size[i]=1;
sort(a+1,a+1+n);
for(int i=n;i;i--) insert(floor(i*1.0/k),i);
for(int i=first[0];i;i=nex[i])
{
qy=size[qx=en[i]];
change(1,1,n);
}
for(int i=1,k=1;i<=n;i=k)
{
while(k<=n && a[i]==a[k]) k++;
for(int j=k-i;j;j--)
{
int pos=find(1,1,n,j);
ans[pos]=a[i];
qy=-size[qx=pos];
change(1,1,n);
for(int l=first[pos];l;l=nex[l])
{
qy=size[qx=en[l]];
change(1,1,n);
}
}
}
for(int i=1;i<=n;i++) write(ans[i]),putchar(' ');
return 0;
}