CF1392G Omkar and Pies
Description
给定两个长度为 \(k\) 的 \(01\) 串,分别是起始串和目标串。
依次给出 \(n\) 个可选操作,第 \(j\) 个操作 \(a_j,b_j\) 表示交换起始串的 \(a_j\) 位置与 \(b_j\) 位置。
你需要选择操作序列中其中连续的一段操作依次按顺序执行,并且选择的操作数量不小于 \(m\) 。
你需要让操作完成后的串与目标串对应相同的位置数量尽可能多。
输出最大数量与你选取操作的区间(若有多种方案输出任意一个即可)。
\(k\le 20,m\le n\le 10^6\)。
Solution
首先执行 \([l,r]\) 的区间操作可以转化为,对起始串进行 \([l,n]\) 操作,再对目标串进行 \([r+1,n]\) 操作,这样一来 \([r+1,n]\) 的操作相当于被抵消了,就跟原问题一样了。
首先可以 \(\mathcal O(nk)\) 预处理出对起始串与目标串执行所有后缀操作得到的序列,设分别为 \(\{a_n\}\) 和 \(\{b_n\}\)。
现在我们等于需要最大化 \(a_i\) 与 \(b_j\) 的相同字符数量,并使 \(j-i\ge m\),设 \(x\) 为 \(a_i\) 中 \(1\) 的个数,\(y\) 为 \(b_j\) 中 \(1\) 的个数,\(z\) 为 \(a_i\) 与 \(b_j\) 同时为 \(1\) 的位置数量,因此对应相同的位置数量就等于:
\[z+(k-x-y+z)=2z+k-x-y \]由于 \(x,y\) 是确定的,因此只需要最大化 \(z\)。枚举最终公共 \(1\) 的位置为 \(s\),求出:
\[dp_{0,s}=\min i[s\subset a_i]\\ dp_{1,s}=\max i[s\subset b_i] \]如果 \(dp_{1,s}-dp_{0,s}\ge m\) ,就可以用 \(popcount(s)\) 来更新答案了,于是预处理 \(a,b,dp\) 的复杂度都是 \(\mathcal O(nk)\)。总复杂度为 \(\mathcal O(nk)\),可以通过此题。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+20;
int n,m,k,a,b,L[N],R[N],c[N],f[N],g[N],tmp[N][21],ret[21];
char s[21],t[21];
int main(){
// freopen("option2.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
scanf("%s",s+1);
int cta=0,ctb=0;
for(int i=1;i<=k;++i){
a=(a<<1)+s[i]-'0';
if(s[i]=='1') cta++;
}
scanf("%s",t+1);
for(int i=1;i<=k;++i){
b=(b<<1)+t[i]-'0';
if(t[i]=='1') ctb++;
}
for(int i=0;i<(1<<k);++i) f[i]=n+1,g[i]=0;
for(int i=1;i<=n;++i) scanf("%d%d",&L[i],&R[i]);
for(int i=1;i<=k;++i) ret[i]=i;
for(int i=n;i>=1;--i){
for(int j=1;j<=k;++j) tmp[i][j]=j;
swap(tmp[i][L[i]],tmp[i][R[i]]);
for(int j=1;j<=k;++j) ret[j]=tmp[i][ret[j]];
a=0;
for(int j=1;j<=k;++j) a=(a<<1)+s[ret[j]]-'0';
f[a]=min(f[a],i);
}
g[b]=n+1;
for(int i=1;i<=k;++i) ret[i]=i;
for(int i=n;i>=1;--i){
for(int j=1;j<=k;++j) tmp[i][j]=j;
swap(tmp[i][L[i]],tmp[i][R[i]]);
for(int j=1;j<=k;++j) ret[j]=tmp[i][ret[j]];
b=0;
for(int j=1;j<=k;++j) b=(b<<1)+t[ret[j]]-'0';
g[b]=max(g[b],i);
}
int ans=-1,reta=0,retb=0;
for(int i=((1<<k)-1);~i;--i){
int ct=0;
for(int j=0;j<k;++j){
if(i&(1<<j)){
ct++;
int tmp=i^(1<<j);
f[tmp]=min(f[tmp],f[i]);
g[tmp]=max(g[tmp],g[i]);
}
}
if(g[i]-f[i]>=m){
int ret=k-cta-ctb+ct+ct;
if(ret>ans) ans=ret,reta=f[i],retb=g[i]-1;
}
}
printf("%d\n%d %d",ans,reta,retb);
return 0;
}