正题
题目链接:https://atcoder.jp/contests/arc132/tasks/arc132_d
题目大意
给出两个恰好有\(n\)个\(1\)和\(m\)个\(0\)的字符串\(s,t\),定义两个字符串距离为通过交换两个相邻的字符把一个变成另一个的最小步数。
对于字符串\(k\)如果\(dis(s,k)+dis(k,t)=dis(s,t)\)那么\(k\)在\(s,t\)之间。
定义一个字符串的权值为相邻的相同字符对数。
求所有在\(s,t\)之间的字符串中权值最大是多少。
\(1\leq n+m\leq 3\times 10^5\)
解题思路
先考虑在\(s,t\)之间的字符串有什么性质,显然的我们对于求\(dis(s,t)\)的时候最优的策略肯定是把\(s\)的第\(i\)个\(1\)移动到\(t\)的第\(i\)个\(1\)处。
也就是对于两个串中第\(i\)个\(1\)的位置记为\(x,y\),那么就是一个一要从\(x\rightarrow y\),也就是在\(s\sim t\)之间的字符串第\(i\)个一肯定在\(x\sim y\)这个范围。
这样我们处理出每个\(1\)的合法区间\(l_i,r_i\),显然的\(l_i\)和\(r_i\)必定递增,因为交换两个\(1\)的顺序必定不划算。所以可以考虑贪心。
不考虑边界的问题,那么我们显然要让\(1\)的连续段尽量少,那么从小到大考虑\(l,r\)如果能和上一个放一起就放一起,不然就放在\(r\)的位置最赚。
然后考虑边界,右边界可以直接判断因为我们肯定是尽量往右放的。左边界的话我们如果\(l_1=1\)我们就分两种情况考虑,也就是第一个放在左边界和第一个放在\(r_1\)两种情况取个最大值。
时间复杂度:\(O(n+m)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) (x&-x)
using namespace std;
const int N=6e5+10;
int A,B,m,n,t[N],l[N],r[N],ans,prt;
char a[N],b[N];
//void Change(int x,int val){
// while(x<=m){
// t[x]+=val;
// x+=lowbit(x);
// }
// return;
//}
//int Ask(int x){
// int ans=0;
// while(x){
// ans+=t[x];
// x-=lowbit(x);
// }
// return ans;
//}
int main()
{
scanf("%d%d",&A,&B);m=A+B;
scanf("%s",a+1);
scanf("%s",b+1);
for(int i=1;i<=m;i++)
if(a[i]=='1')l[++n]=i;
n=0;
for(int i=1;i<=m;i++)
if(b[i]=='1')r[++n]=i;
for(int i=1;i<=n;i++){
if(l[i]>r[i])swap(l[i],r[i]);
}
int last=-1;
for(int i=1;i<=n;i++){
if(l[i]<=last+1)last++;
else ans++,last=r[i];
}
ans=ans*2;
if(last!=m)ans++;
prt=m-ans;
if(l[1]==1){
last=1;ans=1;
for(int i=2;i<=n;i++){
if(l[i]<=last+1)last++;
else ans+=2,last=r[i];
}
if(last!=m)ans++;
ans=m-ans;
prt=max(prt,ans);
}
printf("%d\n",prt);
return 0;
}