题解: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;
}
posted @ 2025-02-11 18:44  BriskCube  阅读(25)  评论(0)    收藏  举报