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;
}