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