CF1870E Another MEX Problem 题解
CF1870E Another MEX Problem 题解
萌新啃了一晚上的官方题解,求管理大大给过/kel
题意
给你一个序列 \(a\),让你选出一些不交的子串,使得它们的 \(MEX\) 的异或和最大。
\(n\le 5000,a_i\le n\)。
思路
考虑 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)\)。
代码
#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;
}

浙公网安备 33010602011771号