题解:P7818 [RC-05] 排列
luogu
题意
够清晰了,不再赘述。
思路
首先,由于是字典序,所以我们要让前几位尽量小,也就是要让第 \(i\) 位取到 \(\min^{\min(n,i+k)}_{j=i}p_j\),假设取到了 第 \(i+o\) 位,取完后相应的让 \(k=k-o\),并把它移到第 \(i\) 位,于是考虑如何维护。
接着,我们可以先维护一个区间和并支持区间加的线段树,因为把第 \(i+o\) 位移到 \(i\) 位,就是把 \([i+o,n]\) 这一段区间内移到未取到的值的最前面所需要的次数减一,我们就可以将第 \(i\) 位移到未取到的值的最前面所需要的次数的初值设为 \(i-1\)。这样,我们查找最小值的区间右端点就可以用二分求出,即二分找到第一个移到未取到的值的最前面所需要的次数大于 \(k\) 的地方 \(r\),然后查找区间的右端点就取 \(r-1\)。
然后,我们再维护一个区间最小值并支持单点修改的线段树,可以查找 \([i,r-1]\) 这一区间内的最小值 \(p_{min}\),并在查询完后将这个最小值压入答案队列并修改为无穷大,这样就不会再取到。然后在找到最小之后就可以在维护的第一个线段树中把 \([min,n]\) 这一段区间内的到未取到的值的最前面所需要的次数都减一。然而上述操作只在维护的第二个线段树中维护一个最小值是不够的,所以还要维护一个编号。
最后,注意到题目中要求的是恰好 \(k\) 次,所以当所有位置都填完后 \(k\) 是一个奇数,那就交换答案的最后两个位置,可以证明这是最优的。然后输出。
代码
//by _maple_leaf_ uid:964876
void push_down(int id,int l,int r){
tr[id<<1]+=tr[id];
tr[id<<1|1]+=tr[id];
tr[id]=0;
}
void build(int id,int l,int r){
if(l==r){
tr[id]=l-1;
return ;
}int mid=l+r>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
void update(int id,int l,int r,int s,int t,int v){
if(s<=l&&r<=t){
tr[id]+=v;
return ;
}push_down(id,l,r);
int mid=l+r>>1;
if(s<=mid)update(id<<1,l,mid,s,t,v);
if(mid<t)update(id<<1|1,mid+1,r,s,t,v);
}
int find(int id,int l,int r,int x){
if(l==r)return tr[id];
push_down(id,l,r);
int mid=l+r>>1;
if(x<=mid)return find(id<<1,l,mid,x);
else return find(id<<1|1,mid+1,r,x);
}
struct node{
int z,id;//维护的编号
bool operator <(const node &b)const{
return z==b.z?id<b.id:z<b.z;
}//重载运算符方便比较
}trr[N<<2];
void init(int id,int l,int r){
if(l==r){
trr[id]={a[l],l};
return ;
}int mid=l+r>>1;
init(id<<1,l,mid);
init(id<<1|1,mid+1,r);
trr[id]=min(trr[id<<1],trr[id<<1|1]);
}
void gx(int id,int l,int r,int x,int v){
if(l==r){
trr[id].z=v;
return ;
}int mid=l+r>>1;
if(x<=mid)gx(id<<1,l,mid,x,v);
else gx(id<<1|1,mid+1,r,x,v);
trr[id]=min(trr[id<<1],trr[id<<1|1]);
}
node get(int id,int l,int r,int s,int t){
if(s<=l&&r<=t){
return trr[id];
}int mid=l+r>>1;
node ret={1919810,114514};
if(s<=mid)ret=min(ret,get(id<<1,l,mid,s,t));
if(mid<t)ret=min(ret,get(id<<1|1,mid+1,r,s,t));
return ret;
}
vector<int>ans;
bool f[N];
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++)a[i]=read();
build(1,1,n);
init(1,1,n);//初始化
for(int i=1;i<=n;i++){
int l=1,r=n;
while(l<r){
int mid=l+r>>1;
if(find(1,1,n,mid)>k)r=mid;
else l=mid+1;
}//二分查找
node tmp;
if(find(1,1,n,r)>k)tmp=get(1,1,n,1,r-1);
else tmp=get(1,1,n,1,r);
ans.push_back(tmp.z);
k-=find(1,1,n,tmp.id);
gx(1,1,n,tmp.id,1919810);//修改为无穷大
update(1,1,n,tmp.id+1,n,-1);
}
if(k&1ll)swap(ans[n-2],ans[n-1]);
for(auto i:ans)write(i,-1);
return 0;
}

浙公网安备 33010602011771号