XXXIV.[九省联考2018]IIIDX
首先,一个非常naive的想法是,建出通关的树出来,然后dfs它,在访问到一个节点时,将现有最小的值赋给它,然后从大到小遍历每个子节点。
这个算法会被 \(d\) 相同的情形叉掉,因为它可以构造出这样一组数据:若某个节点的子树为 \(1\),且它的兄长(指与它有同一父亲,且权值小于它的最大节点)的子树不为 \(1\),且它的值和兄长的值相同。这时,我们可以掏出兄长的一个儿子,将其与该节点交换,并发现树仍然合法,但是字典序变大了。
于是我们开始思考这个问题的本质。
我们发现,用任意一组点集覆盖任意一棵子树,都能保证产生一组解。于是我们萌生了一个念头,能不能从小到大确定每个数的值,且使得剩余的数存在一种合法解?显然这种方法是一定正确的。
我们发现,假如一个点 \(x\) 的子树大小是 \(sz_x\),且赋值为 \(b_i\),则其子树中每个点的权值都 \(\geq b_i\)。这就意味着,对于 \(x\) 的子树,我们需要恰好 \(sz_x\) 个 \(\geq b_i\) 的节点来填满这棵子树。并且,依照我们上面的发现,这是充要条件。
于是问题转换为找到最大的 \(b_i\) 使得存在上述点集。
我们考虑用一棵线段树维护,线段树上每个下标 \(u\) 存了 \(\geq u\) 的尚未被动用的权值数。在确定一个点的权值为 \(b_i\) 后,我们需要对于 \(\leq b_i\) 的所有位置全都减去 \(sz_i\),因为这其中每个位置都确定可用的权值减少了 \(sz_i\)。但是这并不意味着 \(>b_i\) 的权值数就不会变化,因为谁又知道子树中到底选了哪些数呢?
因此,在确定一个 \(b_i\) 时,我们不能简单地找到最大的权值数 \(\geq sz_i\) 的下标,而应找到对于所有 \(v\leq u\),都有 \(v\) 的权值数 \(\geq sz_i\) 的最大 \(u\)。这是很显然的。
而这是线段树二分的常规操作。随便写写就行了。
需要注意的是,确定 \(b_i\) 时的操作的本质是预订,这就意味着当操作真正进行到儿子时,需要把父亲预订的那些位置给退回,同时注意一个父亲只能被退回一次。
时间复杂度 \(O(n\log n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,fa[500100],sz[500100],a[500100],b[500100];
double k;
vector<int>v;
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
struct SegTree{int tag,mn;}seg[2001000];
void pushup(int x){seg[x].mn=min(seg[lson].mn,seg[rson].mn);}
void ADD(int x,int y){seg[x].mn+=y,seg[x].tag+=y;}
void pushdown(int x){ADD(lson,seg[x].tag),ADD(rson,seg[x].tag),seg[x].tag=0;}
void modify(int x,int l,int r,int P,int val){
if(l>P)return;
if(r<=P){ADD(x,val);return;}
pushdown(x),modify(lson,l,mid,P,val),modify(rson,mid+1,r,P,val),pushup(x);
}
int innersearch(int x,int l,int r,int val){
if(l==r)return seg[x].mn>=val?l:l-1;
pushdown(x);
if(seg[lson].mn>=val)return innersearch(rson,mid+1,r,val);
else return innersearch(lson,l,mid,val);
}
int outersearch(int x,int l,int r,int P,int val){
// printf("%d[%d,%d]:%d,%d\n",x,l,r,P,val);
if(r<P)return -1;
if(l>=P){
// printf("[%d,%d]:%d %d\n",l,r,val,seg[x].mn);
if(seg[x].mn>=val)return -1;
return innersearch(x,l,r,val);
}
pushdown(x);
int tmp=outersearch(lson,l,mid,P,val);
if(tmp!=-1)return tmp;
return outersearch(rson,mid+1,r,P,val);
}
bool vis[500100];
void iterate(int x,int l,int r){
if(l==r)printf("%d ",seg[x].mn);
else pushdown(x),iterate(lson,l,mid),iterate(rson,mid+1,r);
}
int main(){
scanf("%d%lf",&n,&k);
for(int i=n,x;i;i--)sz[fa[i]=i/k]+=++sz[i],scanf("%d",&a[i]),v.push_back(a[i]);
sort(v.begin(),v.end()),v.resize(m=unique(v.begin(),v.end())-v.begin());
for(int i=1;i<=n;i++)a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1,modify(1,1,m,a[i],1);
// for(int i=1;i<=n;i++)printf("%d %d\n",i,fa[i]);
// for(int i=1;i<=n;i++)printf("%d ",sz[i]);puts("");
// iterate(1,1,m);puts("");
vis[0]=true,b[0]=1;
for(int i=1;i<=n;i++){
if(!vis[fa[i]])modify(1,1,m,b[fa[i]],sz[fa[i]]-1),vis[fa[i]]=true;
// puts("");
// iterate(1,1,m);puts("");
b[i]=outersearch(1,1,m,b[fa[i]],sz[i]);
if(b[i]==-1)b[i]=m;
// printf("%d:%d\n",sz[i],b[i]);
modify(1,1,m,b[i],-sz[i]);
// iterate(1,1,m);puts("");
}
for(int i=1;i<=n;i++)printf("%d ",v[b[i]-1]);
return 0;
}