CF1870E Another MEX Problem 题解

· · 题解

CF1870E Another MEX Problem 题解

萌新啃了一晚上的官方题解,求管理大大给过/kel

CnBlogs链接

题意

给你一个序列 a,让你选出一些不交的子串,使得它们的 MEX 的异或和最大。

## 思路 考虑 dp。 首先这个东西是异或,不好做最优化 dp。于是我们考虑可行性 dp。 设 $dp_{i,j}$ 表示上一个选的区间的右端点位置 $\le i$ 时,答案是否可以是 $j$。 考虑这个东西怎么转移。我们枚举 $i$,然后枚举 $1\le j\le i$,枚举 $0\le k\le 2n$,从 $dp_{j-1,k\oplus MEX(j,i)}$ 转移到 $dp_{i,k}$。 显然,这样的复杂度是 $O(n^3)$ 的,不可通过。于是我们考虑优化。 我们定义一个东西,叫做好的区间。对于一个区间 $[l,r]$,不存在一个区间 $[l_1,r_1]$,使得 $l\le l_1 \le r_1 \le r$,且 $l\neq l_1$ 或 $r\neq r_1$,且 $MEX(l_1,r_1)=MEX(l,r)$,那么区间 $[l,r]$ 是一个好区间。 有一个结论,就是好区间只有 $O(n)$ 个。 假设我们现在有一个好区间 $[l,r]$,他的 $MEX\neq 0$。我们假设 $a_l>a_r$,因为 $a_l<a_r$ 可以类似地讨论。显然,$a_l\neq a_r$,除非 $a_l=0$。 显然有 $MEX(l,r)>a_l$,因为 $MEX(l,r)$ 肯定不能等于 $a_l$,而且如果 $MEX(l,r)<a_l$,就可以把 $a_l$ 踢出去,那么 $[l,r]$ 就不是一个好区间。 我们假设有一个 $r_1>r$,使得 $[l,r_1]$ 是一个好区间,且 $a_l>a_{r_1}$。 这样可能吗?不可能。因为 $a_l<MEX(l,r),a_{r_1}<a_l$,所以 $a_{r_1}<MEX(l,r)$。这就说明,$a_{r_1}$ 已经在 $[l,r]$ 中出现了,不会产生任何贡献,所以 $[l,r_1]$ 不是一个好区间。 所以我们就证明了,对于每个 $l$,至多有一个 $r$,使得 $a_l>a_r$ 且 $[l,r]$ 是一个好区间。 同时,这也说明了,对于每个 $r$,至多有一个 $l$,使得 $a_l<a_r$ 且 $[l,r]$ 是一个好区间。 而且,如果 $a_i=0$,那么 $i$ 一定不会是上面那两种好区间的端点,但 $[i,i]$ 本身就是一个好的区间,它的 $MEX$ 为 $1$。 综上,好的区间最多只有 $2n$ 个。 那么,我们怎么求出所有好的区间呢? 根据上面的定义,我们对于每个 $l$,找到最小的 $r$,使得 $a_l>a_r$ 且 $MEX(l,r)>a_l$ 即可。反方向也是一样。需要特殊处理一下 $0$。 这样,我们就不用枚举所有区间了,只要枚举好的区间就行了,复杂度 $O(n^2)$。 ## 代码 ```cpp #include<bits/stdc++.h> #define Pi pair<int,int> using namespace std; inline int read(){ int ans=0;bool op=0;char ch=getchar(); while(ch<'0'||'9'<ch){if(ch=='-')op=1;ch=getchar();} while('0'<=ch&&ch<='9'){ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();} if(op)return -ans; return ans; } const int maxn=5010; int n; int a[maxn]; int buc[maxn],mex; vector<Pi>g[maxn]; bool dp[maxn][maxn<<1]; void solve(){ //read n=read(); for(int i=1;i<=n;i++)a[i]=read(); //clear for(int i=1;i<=n;i++)g[i].clear(); //init for(int i=1;i<=n;i++)if(!a[i])g[i].push_back(make_pair(i,1));//特殊处理0 for(int i=1;i<=n;i++){//i是左端点 memset(buc,0,sizeof(int)*(n+5)),mex=0; for(int j=i;j<=n;j++){ buc[a[j]]=1; while(buc[mex])++mex; if(mex>a[i]&&a[j]<a[i]){ g[j].push_back(make_pair(i,mex)); break; } } } for(int i=1;i<=n;i++){ memset(buc,0,sizeof(int)*(n+5)),mex=0; for(int j=i;j>=1;j--){//i是右端点 buc[a[j]]=1; while(buc[mex])++mex; if(mex>a[i]&&a[j]<a[i]){ g[i].push_back(make_pair(j,mex)); break; } } } //solve memset(dp[0],0,sizeof(bool)*((n<<1)+5)); dp[0][0]=1; for(int i=1;i<=n;i++){ memcpy(dp[i],dp[i-1],sizeof(bool)*((n<<1)+5)); for(Pi j:g[i]){ for(int k=0;k<=(n<<1);k++){ if((k^j.second)<=(n<<1))dp[i][k]|=dp[j.first-1][k^j.second]; } } } for(int i=(n<<1);i>=0;i--){ if(dp[n][i]){ cout<<i<<'\n'; return; } } } signed main(){//多组数据 int T=read(); while(T--)solve(); return 0; } ```