首页 > 其他分享 >ABC262F

ABC262F

时间:2022-11-21 16:23:12浏览次数:46  
标签:队列 ll -- hd ABC262F 单调 字典

ABC262F

*2334

卡手的构造题,不是很难想,主要是细节比较多。

题意

给定一个排列 \(p\)。你可以最多执行 \(k\) 次操作。

  • 删除一个数。
  • 将 \(p\) 中末尾的数移到开头。

找出字典序最小的 \(p\)。

题解

显然我们需要贪心地把靠前的位置放上较小的数,考虑怎么把这些小数放到前面去。

不妨先考虑怎么最小化第一位。我们可以在 \(k\) 次操作内将其移到最终排列开头的位置有:

  1. 第 \(1\) 至第 \(k\) 位。只要删掉前面的所有数就好。注意整个过程中不能也没必要执行任何移动操作。
  2. 第 \(n-k+1\) 至第 \(n\) 位。后面的数可以选择移到开头,也可以选择直接删掉,但是在处理完目标位置之后就不能再执行移动操作了。

容易发现,这两类位置是“互斥”的。即,你只能从中选择一种方式构造第一位。显然,我们会选择最小的那一位。

但是由于可能出现 \(\frac{n}{2} \le k\) 的情况,即两个位置集合有交集,导致同一个位置可能有两个构造方式,故需要对两种情况分类讨论。


先来看第一种情况。

由于没有移动,所以等价于直接在原排列上删除 \(k\) 个数然后最小化字典序,直接用单调队列推一次就好了。

具体就是维护一个单调队列,但是全程只能弹出 \(k\) 次,再多就不弹了。由于字典序的性质可以证明这样做的正确性是对的。注意如果最后维护完发现弹出次数还有剩余,即只删除了小于 \(k\) 个数,那么就需要接着从末尾弹直到把次数用完。

原因是字典序中,原串前缀的字典序小于原串。

这样得到的单调队列即为第一种情况下的答案。


再看第二种情况。

我们一定会从第 \(n-k+1\) 至第 \(n\) 位中选择最小的那一位,设该位为 \(p\)。

然后可以发现第 \(p+1\) 位至第 \(n\) 位都是需要操作的,要么被删除要么被移走,不然我们没办法将 \(p\) 移动到开头。

既然怎样都是要操作的,操作次数省不掉,那么我们在找到 \(p\) 之后直接对 \(p\) 至 \(n\) 维护一个标准的单调队列就可以了。单调队列中的元素将被保留至答案中的开头,所以这样可以最小化答案的前面部分。但是这一段中的末尾元素有可能会被后面接上的第 \(1\) 至第 \(p-1\) 位干掉,这个接下来再做。

对于第 \(1\) 至第 \(p-1\) 位,我们还剩下 \(k-(n-p+1)\) 次操作。我们要最小化这个部分的字典序——那么就转化为了第一种情况,做一遍只能弹出 \(k-(n-p+1)\) 次的单调队列即可。对于这部分的末尾同第一部分处理,也是有多余操作就弹到用完为止。

然后我们发现这样的单调队列的第一位是有可能比前面 \(p\) 至 \(n\) 位得到的单调队列的末尾更优的,所以要检查一下,如果更优那么第 \(p\) 至 \(n\) 位得到的单调队列就需要弹出末尾元素。

最后把 \(p\) 至 \(n\) 位得到的单调队列和 \(1\) 至 \(p-1\) 位的单调队列拼起来就是这种情况下的答案。


最后,比较两种情况下答案的字典序,选择更优的打印即可。

时间复杂度 \(O(n)\) 。

代码

相对简单,但是有不少细节。

const ll maxn=2e5+5;
ll n,k,a[maxn];
ll q[maxn],tp;
ll b[maxn],hd;
vector<ll>ans1;
vector<ll>ans2;
void solve(){
	n=R,k=R;
	for(ll i=1;i<=n;i++){
		a[i]=R;
	}
	// 第一种情况
	for(ll i=1,cnt=0;i<=n;i++){
		while(tp&&a[i]<a[q[tp]]&&cnt<k)tp--,cnt++;//最多弹出k次
		q[++tp]=i;
		if(i==n){
			while(tp&&cnt<k)cnt++,tp--;//注意在最后一位的剩余操作
		}
	}
	for(ll i=1;i<=tp;i++)ans1.push_back(a[q[i]]);//记录答案
	// 第二种情况
	tp=0;
	a[n+1]=1ll<<60;
	ll p=n+1;
	//找到 p
	for(ll i=n;i>n-k;i--){
		if(a[i]<a[p])p=i;
	}
	//单调队列,弹出次数不会超过k次
	for(ll i=p;i<=n;i++){
		while(tp&&a[i]<a[q[tp]])tp--;
		q[++tp]=i;
	}
	k-=(n-p+1);//减去已经消耗的次数
	for(ll i=1;i<p;i++){
		while(hd&&k&&a[b[hd]]>a[i])hd--,k--;
		b[++hd]=i;//第 1 至 p-1 位的单调队列
	}
	while(k)hd--,k--;//如果操作次数还有剩余,同第一种情况
	while(hd&&tp&&a[q[tp]]>a[b[1]])tp--;//如果更优
	for(ll i=1;i<=tp;i++)ans2.push_back(a[q[i]]);
	for(ll i=1;i<=hd;i++)ans2.push_back(a[b[i]]);//记录答案
	if(ans1<ans2)for(auto it:ans1)wk(it);
	else for(auto it:ans2)wk(it);//选择更优的打印
	return ;
}

标签:队列,ll,--,hd,ABC262F,单调,字典
From: https://www.cnblogs.com/rnfmabj/p/abc262f.html

相关文章