题解 P4364 【[九省联考2018]IIIDX】
dengyaotriangle · · 题解
UPD 2021/3/26 感谢 @LiM_817 发现了一些笔误,已修改
我认为其它题解中
首先很容易想到一个(只在
正解则是考虑按位确定:考虑从
- 使得这个东西确定之后,存在一种
i+1 到n 的分配方案,使得总方案合法。 - 尽可能大。
容易证明这个过程结束之后取到的就是字典序最大的合法方案。(这个方法对于其它求字典序最大值的方案也适用,是一个非常套路的东西)
那么接下来的思路将会是这样的:
- 既然是逐位确定,那我们考虑动态确定,动态维护接下来确定什么合法
- 确定了一个点不仅仅确定了这个值,其实相当于限制了它的子树内的点都
\geq x ,或者说,这是对之后的尚未确定的值进行限定。 - 我们发现上述限定是充分必要的,于是可以直接维护它作为是否合法的判据。
- 根据限制满足的贪心性质,发现这个东西可以奇妙地使用线段树维护。
考虑本题中我们如何判定一个值的选取是否合法。
若我们新确定了某个位置的值,考虑确定了它之后,会怎么影响接下来的点取值的合法性。
考虑限制:一个子树内的所有点要
令一个点子树大小为
那我们知道,若这个位置
而且这个条件还是充分必要的:若找得到
而我们发现我们确定
所以我们引入的限制就形如:需要拿走
但是我们发现,这个条件其实需要在确定
也就是说,每当我们发现一个节点的父亲有着上述限制,须将其删除,并且换成一个 “需要拿走一个
而直观上来讲,我们干的事情其实是:把整棵子树的限制,分成根的限制和这颗子树挂在根上每棵子树的限制。
所以我们还需要支持限制的 删除操作,以及形如 需要拿走一个
限制说完了,怎么判断哪些值满足限制?若我们现在想给
我们发现,我们只需要找到最大的值
考虑令
原因是因为为了让剩下的
那么我们发现我们已经会求这个”最大的合法值了“:考虑使用线段树维护
而最后剩下的一个操作,需要拿走一个
而
其实这个操作和另一个修改操作的形式一模一样,但一个是
参考代码:
#include<bits/stdc++.h>
using namespace std;
//dengyaotriangle!
const int maxn=500005;
int n,m;double k;
int val[maxn],sval[maxn],cnt[maxn];
struct node{
int tg,mi;
node* c[2];
}pool[maxn<<1];int ps;
void pu(node* rt){
rt->mi=min(rt->c[0]->mi,rt->c[1]->mi);
}
void pd(node* rt){
rt->mi+=rt->tg;
if(rt->c[0]){
rt->c[0]->tg+=rt->tg;
rt->c[1]->tg+=rt->tg;
}
rt->tg=0;
}
node* bt(int l,int r){
node* nw=pool+ps++;
if(l==r){
nw->mi=cnt[l];
}else{
nw->c[0]=bt(l,(l+r)>>1);nw->c[1]=bt(((l+r)>>1)+1,r);
pu(nw);
}
return nw;
}
void chg(node* rt,int cl,int cr,int l,int r,int d){
int cm=(cl+cr)>>1;
pd(rt);
if(l==cl&&r==cr)rt->tg+=d,pd(rt);
else if(r<=cm)chg(rt->c[0],cl,cm,l,r,d),pd(rt->c[1]),pu(rt);
else if(l>cm)chg(rt->c[1],cm+1,cr,l,r,d),pd(rt->c[0]),pu(rt);
else chg(rt->c[0],cl,cm,l,cm,d),chg(rt->c[1],cm+1,cr,cm+1,r,d),pu(rt);
}
int find(node* rt,int cl,int cr,int s){
int cm=(cl+cr)>>1;
pd(rt);
if(cl==cr){return cl-1;}
else{
pd(rt->c[0]);int w=rt->c[0]->mi;
if(w<s)return find(rt->c[0],cl,cm,s);
else return find(rt->c[1],cm+1,cr,s);
}
}
int ans[maxn];
int sz[maxn];
bool vis[maxn];
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>val[i];
sort(val+1,val+1+n);
for(int i=1;i<=n;i++)sval[i]=val[i];
m=unique(sval+1,sval+1+n)-sval;
for(int i=1;i<m;i++)cnt[i]=upper_bound(val+1,val+1+n,sval[i])-lower_bound(val+1,val+1+n,sval[i]);
for(int i=m-1;i>=1;i--)cnt[i]+=cnt[i+1];
node* rt=bt(1,m);
for(int i=n;i>=1;i--){
sz[i]++;
sz[(int)floor(i/k)]+=sz[i];
}
vis[0]=1;
for(int i=1;i<=n;i++){
int fa=(int)floor(i/k);
if(!vis[fa]){
vis[fa]=1;
chg(rt,1,m,1,ans[fa],sz[fa]-1);
}
int v=find(rt,1,m,sz[i]);
ans[i]=v;
chg(rt,1,m,1,v,-sz[i]);
}
for(int i=1;i<=n;i++)cout<<sval[ans[i]]<<' ';
return 0;
}