题目大意
给你长度为n的一个序列a,以及一个参数K,需要把a重新排列,使得
a[i]>=a[⌊ik⌋]
a
[
i
]
>=
a
[
⌊
i
k
⌋
]
。求字典序最大的重排序列。
解题思路
贪心。
我们从前往后放,每次放合法的尽量大的。
把大小关系抽象成树形关系。其中i的父亲是i/k。
首先考虑d[i]互不相同。
一开始什么都没放,第一个数我们选第size[1]大的数填,这样才能保证1号点的子树的元素都比它大,暂时屏蔽前size[1]-1个点,表示预留出来给子树填。接着考虑第二个,假如fa[2]!=1,那么屏蔽的点相当于不存在,同样地做即可。如果父亲进行了屏蔽操作,要把操作撤销一下。这个用权值线段树即可实现。当然也可以直接打个dfs做。
那么现在要考虑d[i]有相同的情况。(先把d降序排序)
比如这种情况:
9 8 7 6 5 5 5 5 5 4 3 2
假如1号的size为7,那么按理说前7个应该屏蔽。但是,如果2号不是1的儿子,这样不是最优的,应该以最后一个5为结尾,屏蔽后面的7个。这样才能使2号尽量大。(比如二号只有一个儿子,那么填9是可行的)
权值线段树是不能用了,想别的办法维护。
设f[i]表示d中,下标i以及其之前的位置,一定预留掉了的数量。比如上面,如果1填数,那么我们要给最后一个5到末尾的位置的f都加上7,表示在[1,8]中选出7个数。而前面不用管。毕竟,我们对i-f[i]做个后缀min,就可以得出一个位置前面至多有多少个位置能用。
我们用线段树维护i-f[i]的后缀min,这样我们可以快速得到一个位置前面还有多少个数能用。
每次我们填x的时候,要找到最前面的,后缀min{i-f[i]}>=size[x]的下标i,如果有和他相同的元素,使用相同元素下标最大的可用的i。就比如上面定位到了第3个5,我们使用第5个5,并且把第五个5标记为不可用。这样就可以维护了。
记得把父亲的预留撤销一下。
扫一遍就可以了。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=2e6+5;
int czy[N],a[N],id[N],fa[N],tr[N*4],prt[N],n,col,siz[N],z,i,d[N],q1,q2,x,dfn[N],td,cnt,q3,dis[N],dec,f[N],e[N];
db K;
int fst[N],nxt[N],b[N],tt;
void cr(int x,int y)
{
tt++;
b[tt]=y;
nxt[tt]=fst[x];
fst[x]=tt;
}
bool cmp(int x,int y)
{
return a[x]<a[y];
}
void lsh()
{
int i;
fo(i,1,n) id[i]=i;
sort(id+1,id+1+n,cmp);
fo(i,1,n)
{
if (czy[a[id[i-1]]]!=a[id[i]]) col++;
czy[col]=a[id[i]];
a[id[i]]=col;
}
}
void change(int x,int l,int r,int p,int v)
{
if (l==r) tr[x]+=v;
else
{
int m=l+r>>1;
if (p<=m) change(x*2,l,m,p,v);
else change(x*2+1,m+1,r,p,v);
tr[x]=tr[x*2]+tr[x*2+1];
}
}
int grab(int x,int l,int r,int v)
{
if (l==r) return l;
int m=l+r>>1;
if (tr[x*2+1]>=v) return grab(x*2+1,m+1,r,v);
else return grab(x*2,l,m,v-tr[x*2+1]);
}
int process(int x,int l,int r,int p,int v,int id)
{
if (l==r)
{
dec=min(tr[x],v);
v-=dec;
change(1,1,n,p,-dec);
e[++e[0]]=p;
f[e[0]]=dec;
return v;
}
int m=l+r>>1;
if (p<=m) return process(x*2,l,m,p,v,id);
else return process(x*2+1,m+1,r,p,v,id);
}
void dfs(int x)
{
siz[x]=1;
dfn[x]=td++;
for(int p=fst[x];p;p=nxt[p])
{
dis[b[p]]=dis[x]+1;
dfs(b[p]);
siz[x]+=siz[b[p]];
}
}
void solve(int x)
{
z=grab(1,1,n,cnt+siz[x]);
prt[x]=z;
change(1,1,n,z,-1);
}
int main()
{
freopen("iiidx.in","r",stdin);
freopen("iiidx.out","w",stdout);
scanf("%d %lf",&n,&K);
fo(i,1,n) scanf("%d",a+i);
lsh();
fo(i,1,n) change(1,1,n,a[i],1);
fo(i,1,n)
fa[i]=trunc(db(i)/K+1e-4);
fd(i,n,1) cr(fa[i],i);
dfs(0);
d[1]=0;
q1=1;
q2=1;
while (q1<=q2)
{
q3=q2;
cnt=0;
fo(i,q1,q3)
{
x=d[i];
if (x==26666)
{
a[0]=0;
}
if (x)
{
solve(x);
cnt+=process(1,1,n,prt[x],siz[x]-1,dis[x]);
}
for(int p=fst[x];p;p=nxt[p])
{
dis[b[p]]=dis[x]+1;
d[++q2]=b[p];
}
}
while (e[0])
{
change(1,1,n,e[e[0]],f[e[0]]);
e[0]--;
}
q1=q3+1;
}
fo(i,1,n)
printf("%d\n",czy[prt[i]]);
}