题解:[省选联考 2026] 排列游戏 / perm

题目传送门

题意分析

记答案为 \(p\)\(p\) 的第 \(l\sim r\) 个元素构成的集合为 \(p[l,r]\)

其实本题纯靠对 \(\operatorname{mex}\) 的理解,因此本文先分析一下 \(\operatorname{mex}\) 到底是个什么东西。

\[\operatorname{mex} S=\min\overline S \]

\(\operatorname{mex}\) 等价于补集 \(\min\),这是比较好得到的。因为 \(\overline S\) 中的数一定没有在 \(S\) 中出现过。唯一一种例外是 \(\overline S=\varnothing\) 的时候,也比较好特判。

之后再看序列上的 \(\operatorname{mex}\),钦定序列下标为 \(0\sim n-1\)。对于 \(f=\operatorname{mex},\min\),记 \(f(l,r)=f(p[l,r])\),有:

\[\begin{aligned} \operatorname{mex}(l,r)&=\min(\min(0,l-1),\min(r+1,n-1))\\ &=\min(\operatorname{mex}(0,r),\operatorname{mex}(l,n-1)) \end{aligned} \]

由此可以得到:

\[\begin{aligned} \operatorname{mex}(0,r)&=\min(r+1,n-1)\\ \operatorname{mex}(l,n-1)&=\min(0,l-1) \end{aligned} \]

于是得到了一个结论:前缀 \(\operatorname{mex}\) 等价于后缀 \(\min\),后缀 \(\operatorname{mex}\) 等价于前缀 \(\min\)

则:只需要知道所有的前/后缀 \(\operatorname{mex}\),就可以知道所有的 \(\operatorname{mex}\)

回归本题。\(2n-2\) 次询问得到所有的前/后缀最小值 \(\textit{pre}_i,\textit{suf}_i\) 后,即有:

\[\operatorname{mex}(l,r)=\min(\textit{pre}_{l-1},\textit{suf}_{r+1}) \]

考虑所有的 \(\operatorname{mex}\) 限制等价于限制了前缀最小值 \(\textit{pre}_i\) 和后缀最小值 \(\textit{suf}_i\)\(\operatorname{mex}\) 本身已经不重要。

\(0\) 位置为 \(\textit{pos}_0\)。以 \(0\) 为分界点,\(\textit{pre}_i,\textit{suf}_i\) 的限制就是一个中间低两边高的形式,因此从大至小、从两边至中间贪心放置即可。

因此得到了一个 \(2n-2\) 次询问的做法。

容易发现 \(0\) 左边的 \(\textit{pre}_i\) 才有意义,右边的 \(\textit{suf}_i\) 才有意义。因此可以在计算 \(\textit{pre}_i\) 时找 \(0\),然后计算 \(0\) 右边的 \(\textit{suf}_i\) 即可。

总询问次数 \(n\),时间复杂度 \(\mathcal O(n)\)

注意特判 \(0\) 在边上的情况,否则你会 100pts \(\rightarrow\) 20pts。

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
void init(int c,int t){
	
}
vector<int>perm(int n){
	vector<int>p(n,-1),vis(n),pre(n),suf(n);
	int pos0=n-1;
	for(int i=0;i<n-1;i++){
		pre[i]=query(i+1,n-1);
		if(pre[i]==0){
			pos0=i;
			suf[i]=0;
			for(i++;i<n;i++){
				suf[i]=query(0,i-1);
			}
			break;
		}
	}
	p[pos0]=0;
	vis[0]=1;
	p[0]=pre[0],p[n-1]=suf[n-1];
	vis[p[0]]=vis[p[n-1]]=1;
	for(int i=1;i<n-1;i++){
		if(pre[i-1]!=pre[i]){
			p[i]=pre[i];
			vis[p[i]]=1;
		}
	}
	for(int i=n-2;0<=i;i--){
		if(suf[i]!=suf[i+1]){
			p[i]=suf[i];
			vis[p[i]]=1;
		}
	}
//	for(int i:p){
//		cerr<<i<<'\n';
//	}
	for(int l=0,r=n-1,i=n-1;i>0;i--){
		while(0<=i&&vis[i]){
			i--;
		}
		if(i<0){
			break;
		}
		while(l<pos0&&p[l]!=-1){
			l++;
		}
		while(pos0<r&&p[r]!=-1){
			r--;
		}
		if(pre[l]>suf[r]){
			p[l]=i;
		}else{
			p[r]=i;
		}
	}
//	for(int i:p){
//		cerr<<i<<' ';
//	}
//	cerr<<'\n';
	return p;
}

闲话

以前对于 \(\operatorname{mex}\) 接触不多,并且有 \(\operatorname{mex}\) 的除了莫队都很难。没怎么研究过,也不知道如何刻画。结果场上最终 32.5pts……

这么简单的题我为什么写不出来……

posted @ 2026-05-03 17:29  TH911  阅读(25)  评论(0)    收藏  举报