题面
求出每个子树的siz
数由大到小排
靠谱的策略大概是第i个节点选排名为
siz[i]
s
i
z
[
i
]
的数 的与之相等的 排名最大的那个
然后为它的子树预留足够的点
但是不知道要预留那些点
据题解,可以对于每个点维护该点的前缀有几个可以选,记为
c[i]
c
[
i
]
对于点x,找到最小的
i
i
使得所有的满足
c[j]≥siz[x]
c
[
j
]
≥
s
i
z
[
x
]
,再找与之相等的 排名最大的那个。
由于预留的都比它大,故对于所有的
j≥i
j
≥
i
,
c[j]−=siz[x]
c
[
j
]
−
=
s
i
z
[
x
]
线段树维护
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=500500;
int n,a[N],cnt[N];
int fa[N],siz[N];
int t[N<<2],lazy[N<<2];
int ans[N];
double k;
bool cmp(int x,int y)
{
return x>y;
}
void down(int ro)
{
if(lazy[ro])
{
t[ro*2]+=lazy[ro];
t[ro*2+1]+=lazy[ro];
lazy[ro*2]+=lazy[ro];
lazy[ro*2+1]+=lazy[ro];
lazy[ro]=0;
}
}
void build(int ro,int l,int r)
{
if(l==r)
{
t[ro]=l;
return;
}
int mid=(l+r)/2;
build(ro*2,l,mid);
build(ro*2+1,mid+1,r);
t[ro]=min(t[ro*2],t[ro*2+1]);
}
void update(int ro,int l,int r,int zuo,int you,int ad)
{
if(l>=zuo&&r<=you)
{
lazy[ro]+=ad;
t[ro]+=ad;
return;
}
if(l>you||r<zuo)
return;
down(ro);
int mid=(l+r)/2;
update(ro*2,l,mid,zuo,you,ad);
update(ro*2+1,mid+1,r,zuo,you,ad);
t[ro]=min(t[ro*2],t[ro*2+1]);
}
int query(int x)
{
int ro=1,l=1,r=n;
while(l!=r)
{
down(ro);
int mid=(l+r)/2;
if(t[ro*2+1]>=x)
{
ro=ro*2;
r=mid;
}
else
{
ro=ro*2+1;
l=mid+1;
}
}
if(t[ro]>=x)
return l;
return l+1;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
fa[i]=(int)floor(i/k),siz[i]=1;
for(int i=n;i;i--)
siz[fa[i]]+=siz[i];
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1,cmp);
for(int i=n-1;i>=1;i--)
if(a[i]==a[i+1])
cnt[i]=cnt[i+1]+1;
build(1,1,n);
for(int i=1;i<=n;i++)
{
if(fa[i]&&fa[i]!=fa[i-1])
update(1,1,n,ans[fa[i]],n,siz[fa[i]]-1);
int hy=query(siz[i]);
ans[i]=hy;
hy+=cnt[hy];
cnt[hy]++;
hy-=cnt[hy]-1;
ans[i]=hy;
update(1,1,n,hy,n,-siz[i]);
}
for(int i=1;i<=n;i++)
printf("%d ",a[ans[i]]);
cout<<endl;
return 0;
}