题解:【ARC132D】 Between Two Binary Strings
首先我们记要寻找的“位于 \(s\) 和 \(t\) 之间的字符串”为 \(c\) 串,\(s\) 串中的第 \(i\) 个 \(1\) 位置为 \(cnts_i\),\(t\) 串中的第 \(i\) 个 \(1\) 位置为 \(cntt_i\),\(c\) 串中的第 \(i\) 个 \(1\) 位置为 \(cntc_i\),根据定义这个 \(c\) 串需要满足 \(\min(cnts_i,cntt_i) \le cntc_i \le \max(cnts_i,cntt_i)\)。
容易想到我们要使答案尽可能大,就要尽可能多的让相同的数字凑在一起。考虑贪心,将 \(1\) 插入 \(0\) 中,由于 \(c\) 串所要满足的性质定下了每一个 \(1\) 可以放置的区间,所以我们应该将每一段的 \(1\) 尽可能向后放,然后检查后面的每一段能不能接着放 \(1\),如果可以放则放,不可以则重新另起一段,再从这一段的最后面开始放 \(1\)。
满心欢喜的交上去,你会发现红了一片,这是为什么捏?
考虑这么一组数据:
1 1 1 1 0 0
0 1 1 1 1 0
我们如果按照上面的贪心策略,会得到答案 0 1 1 1 1 0
,但显然 1 1 1 1 0 0
更优。这是因为如果我们在第一位上可以放 \(1\),并且有一个串的前面有一部分 \(0\),第一段 \(1\) 的后面又有一部分 \(0\) 时,我们把 \(1\) 往后放的策略就会使两段 \(0\) 分开,这时就应该把第一段 \(1\) 放在最前头,使得保证在同等贡献数量的 \(1\) 的情况下,\(0\) 的贡献也尽可能大。也非常好处理,让第一个 \(1\) 在最开头就好了,剩下的还上面的贪心一致。
实现大概就是单指针,只需要枚举放 \(1\) 的位置,遍历一遍存储答案的数组统计即可,时间复杂度 \(O(n)\)。
\(Code\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace LgxTpre
{
static const int MAX=500010;
static const int INF=20070707070707;
int n,m,len;
char a[MAX],b[MAX];
int posa[MAX],cnta;
int posb[MAX],cntb;
int L[MAX],R[MAX];
int num[MAX],ans;
inline int getans(int begin)
{
int sum=0;
memset(num,0,sizeof num);
num[begin]=1;
for(int i=2,last=begin;i<=m;++i)
if(last+1>=L[i]&&last+1<=R[i])
++last,num[last]=1;
else
last=R[i],num[last]=1;
for(int i=1;i<len;++i)
if(num[i]==num[i+1])
++sum;
return sum;
}
inline void lmy_forever()
{
scanf("%lld%lld",&n,&m); len=n+m;
scanf("%s",a+1); scanf("%s",b+1);
for(int i=1;i<=len;++i) if(a[i]=='1') posa[++cnta]=i;
for(int i=1;i<=len;++i) if(b[i]=='1') posb[++cntb]=i;
for(int i=1;i<=m;++i)
L[i]=min(posa[i],posb[i]),
R[i]=max(posa[i],posb[i]);
ans=max(ans,getans(R[1]));
if(L[1]==1) ans=max(ans,getans(L[1]));
cout<<ans;
return;
}
}
signed main()
{
LgxTpre::lmy_forever();
return (0-0);
}