[CEOI 2025] Equal Mex 题解

[CEOI 2025] Equal Mex

虽然说是套路题,但是记录一下一些结论防止自己以后忘了。

首先不难发现你划分出的每个子段的 \(\operatorname{mex}\) 一定就是整个区间的 \(\operatorname{mex}\),而且 \(k\) 合法 \(k-1\) 也一定合法,所以答案就是能划分的最大段数,不难写出 \(O(nq)\) 的贪心。

然后考虑正解,我们需要若干结论:

结论一:我们称一个区间 \([l,r]\)极小 \(\operatorname{mex}\) 区间当且仅当不存在他的一个子区间 \([l',r']\) 使得 \(\operatorname{mex}[l,r]=\operatorname{mex}[l',r']\)。则极小 \(\operatorname{mex}\) 区间的数量是 \(\le 2n\)(为了避免 corner case 我们不讨论 \(l=r\) 的区间,这些区间的处理是简单的)。

证明:不妨先考虑所有 \(a_l>a_r\) 的区间,对于这样的一个极小 \(\operatorname{mex}\) 区间 \([l,r]\),由于他是极小的,所以 \(\operatorname{mex}[l,r]>a_l>a_r\),否则可以缩减某一个端点,那么:
- 对于一个 \(r'<r,a_{r'}<a_l\)\([l,r']\) 不可能是极小的,因为 \(\operatorname{mex}[l,r']\le a_r < a_l\) 所以增大 \(l\) 不影响区间 \(\operatorname{mex}\)
- 对于一个 \(r'>r,a_{r'}<a_l\)\([l,r']\) 同样不可能是极小的,因为 \(\operatorname{mex}[l,r]>a_l>a_{r'}\) 所以 \(a_{r'}\)\([l,r]\) 出现过了,减小 \(r'\) 不影响区间 \(\operatorname{mex}\)
因此对于每个 \(l\) 只存在一个 \(r\) 满足 \(a_r<a_l\)\([l,r]\) 是极小 \(\operatorname{mex}\) 区间;同理可证对每个 \(r\) 只有一个 \(l\) 满足 \(a_l<a_r\) 且符合条件。

然后是如何 \(O(n\log n)\) 求出所有极小 \(\operatorname{mex}\) 区间:对区间左端点 \(l\) 从小到大扫描线,用 ODT 维护每个 \(r\) 对应的区间 \([l,r]\)\(\operatorname{mex}\)(显然 \(\operatorname{mex}\) 具有单调性,可以用 ODT 维护);每次 \(l\to l+1\) 时,不妨设 \(x\) 是下一个满足 \(a_x=a_l\) 的位置,则我们需要把 \([l,x)\) 末尾的若干 \(\operatorname{mex} > a_l\) 的区间推平成 \(a_l\),如果在这个过程中推平了一个区间 \([l',r']\),那么 \([l,l']\) 就是一个极小 \(\operatorname{mex}\) 区间;我们可以顺便求出每个询问区间的 \(\operatorname{mex}\)
注意: 如果 \([l,x)\) 末尾不存在需要推平的区间,那么你可能需要把 ODT 一开始 split 出的两个区间 merge 回去,因为我们要保证 ODT 中维护的颜色段是极长的,否则求出的极小 \(\operatorname{mex}\) 区间会有问题。

结论二:对于一个区间 \([l,r]\) 总可以找到一个极小 \(\operatorname{mex}\) 区间 \([l',r']\subset [l,r]\) 满足 \(\operatorname{mex}[l',r']=\operatorname{mex}[l,r]\)

我们把所有 \(\operatorname{mex}\) 相同的询问和极小 \(\operatorname{mex}\) 区间放到一起做,于是问题变成找到询问区间内尽可能多的互不相交的极小 \(\operatorname{mex}\) 区间。
显然这是个经典贪心,由于所有极小 \(\operatorname{mex}\) 区间互不包含,所以把这些区间按照左端点排序右端点也升序,先用双指针预处理每个区间跳到的下一个区间,回答询问可以直接倍增。

\(O((n+q)\log n)\),目前最优解第二。

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
#define eb emplace_back
#define pb push_back
#define iter set<P>::iterator 
#define PII pair<int,int>
#define fi first
#define se second 
using namespace std;
const int N=6e5+5,V=4e5+5;
int n,m,a[N],pos[V],nxt[N],ans[N]; 
bool flag[V];
struct Que{ int l,r; } que[N];
vector<int> vec[N];
struct P{ mutable int l,r,c; };
bool operator < (const P&x,const P&y){ return x.l<y.l; }
struct ODT{
	set<P> S;
	iter split(int x){
		if(x==n+1) return S.end();
		iter it=S.lower_bound({x,0,0});
		if(it!=S.end()&&(*it).l==x) return it;
		--it;
		int l=(*it).l,r=(*it).r,c=(*it).c;
		S.erase(it),S.insert({l,x-1,c});
		return S.insert({x,r,c}).first;
	}
	int query(int x){
		iter it=S.lower_bound({x,0,0});
		if(it!=S.end()&&(*it).l==x) return (*it).c;
		else return (*prev(it)).c;
	}
	void pop_front(){
		if((*S.begin()).l==(*S.begin()).r) S.erase(S.begin());
		else (*S.begin()).l++;
	}
}odt;
int f[25][N];
struct Solve{
	vector<PII> v;
	vector<int> q;
	void work(int mex){
		if(!q.size()) return;
		if(mex==1){
			for(int id:q) ans[id]=que[id].r-que[id].l+1;
			return;
		}
		sort(v.begin(),v.end());
		int siz=v.size();
		for(int i=0,j=0;i<siz;i++){
			while(j<siz&&v[j].fi<=v[i].se) j++;
			f[0][i]=j;
		}
		f[0][siz]=siz;
		for(int t=1;t<=__lg(siz);t++) for(int i=0;i<=siz;i++) f[t][i]=f[t-1][f[t-1][i]];
		for(int id:q){
			int l=que[id].l,r=que[id].r,x=lower_bound(v.begin(),v.end(),make_pair(l,0))-v.begin();
			ans[id]=1;
			for(int t=__lg(siz);t>=0;t--)
				if(f[t][x]<siz&&v[f[t][x]].se<=r) ans[id]+=1<<t,x=f[t][x];			
		}
	}
}sub[V];
void Init(){
	for(int i=n;i>=1;i--) nxt[i]=(pos[a[i]])?pos[a[i]]:(n+1),pos[a[i]]=i;
	int mex=1,lst=1;
	for(int i=1;i<=n;i++){
		flag[a[i]]=true;
		if(flag[mex]){
			if(i-1>=lst) odt.S.insert({lst,i-1,mex});
			lst=i;	
		}
		while(flag[mex]) mex++;
	}
	odt.S.insert({lst,n,mex});
	
	for(int l=1;l<=n;l++){
		for(int id:vec[l]) sub[odt.query(que[id].r)].q.eb(id);
		int r=nxt[l];
		iter itr=odt.split(r);
		if((*prev(itr)).c<a[l]){
			if(r!=n+1&&(*prev(itr)).c==(*itr).c){
				(*itr).l=(*prev(itr)).l;
				odt.S.erase(prev(itr));
			} 
		}
		else{
			--itr;
			int L,R=(*itr).r;
			while(true){
				sub[(*itr).c].v.pb({l,(*itr).l});
				if(itr==odt.S.begin()||(*prev(itr)).c<a[l]){
					L=(*itr).l;
					odt.S.erase(itr);
					break;	
				}
				--itr,odt.S.erase(next(itr));
			}
			odt.S.insert({L,R,a[l]});
		}
		odt.pop_front();
	}
}
vector<int> solve(int N, std::vector<int>& v,int Q, std::vector<std::pair<int, int>>& queries){
	n=N,m=Q;
	for(int i=1;i<=n;i++) a[i]=v[i-1];
	for(int i=1;i<=m;i++) que[i]={queries[i-1].fi,queries[i-1].se},vec[que[i].l].eb(i);
	Init();
	for(int i=1;i<V;i++) sub[i].work(i);
	vector<int> res;
	for(int i=1;i<=m;i++) res.eb(ans[i]);
	return res;
}
posted @ 2025-11-27 19:31  Green&White  阅读(5)  评论(0)    收藏  举报