题目链接:https://agc019.contest.atcoder.jp/tasks/agc019_d
题意:
对01字符串A有三种操作:左移一位、右移一位,若另一个01字符串B的某一位为1,则A的该位可以 xor 1,问A到B至少要几次操作
吐槽:
这几天做了不少题。。懒得放上来了,写几个比较好的题吧。。
第6次把while打成if…于是自闭了好久
题解:
首先枚举A最后在什么位置与B重合,设最终位置为S
对比A[i]与B[(i+S)%n],就知道哪些位需要异或了
可以预处理出B的所有位向左向右(假设是一个环)所到的第一个1处,显然到第二个及以后的1不会更优,这就是可能需要A对应位异或1的位置
设第cur位左右分别到达的位置是left、right(之所以都需要走到左、右是因为如果只向左(右)走的话,有可能一些位置走不到(或距离变大)),则left<=cur<=right,有一个显然的关系:最终走的路程=2*(right-left)-|S|。证明就是按照S>=0 && S<0 分类讨论一下,画下图就很明白了吧…【注:这里的S>=0是参照cur来说的,实际上就是S>=cur && S<cur】
当S>=0时,ans=2*(right-left)-S,但是这里left<S && right>=S不太好处理,有一种神奇的方法:令X=-left,Y=right-S,则原式=2*(Y+X+S)-S=2*(Y+X)+S,且X>=0 && Y>=0,直接上手维护即可
这题数组下标以0开始为好,不然细节很多
代码:
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#include <string>
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=100005;
char a[maxn],b[maxn];
int ans=inf;
int lf[maxn],ri[maxn],mx[maxn];
int n;
void solve(){
memset(ri,0,sizeof ri);memset(lf,0,sizeof lf);
for(int i=0;i<n;i++){
while(b[(i+ri[i])%n]=='0')++ri[i];
while(b[(i+lf[i]+n)%n]=='0')--lf[i];
}
for(int s=0;s<n;s++){
memset(mx,0,sizeof mx);
int dif=0;
for(int r=0;r<n;r++){
if(a[r]!=b[(r+s)%n]){
mx[-lf[r]]=max(mx[-lf[r]],ri[r]-s),++dif;
}
}
int cur=0,tmp=inf;
for(int i=n-1;i>=0;i--){
tmp=min(tmp,cur+i);
cur=max(cur,mx[i]);
}
ans=min(ans,2*tmp+s+dif);
}
}
int main(){
scanf("%s",a);scanf("%s",b);
n=strlen(a);
int cnta=0,cntb=0;
for(int i=0;i<n;i++)if(a[i]=='1')++cnta;
for(int i=0;i<n;i++)if(b[i]=='1')++cntb;
if(cntb==0){ // 特判
if(cnta==0)puts("0");
else puts("-1");
return 0;
}
solve();
reverse(a,a+n);
reverse(b,b+n);
solve();
printf("%d\n",ans);
return 0;
}