【agc019D】Shift and Flip
Description
给你一个\(A\)串一个\(B\)串(长度相等的两个\(01\)串),一次操作可以选择将\(A\)向左循环移动一位,将\(A\)向右循环移动一位,或者选择某个满足\(B_i=1\)的\(i\),将\(A_i\)反转(\(0\)变\(1\),\(1\)变\(0\))
目标是让两个串相等,求最小的操作次数,无解就\(-1\)
Solution
首先判掉不需要操作的情况:如果两个串一开始就相等了就不用操作了
然后再判掉无解的情况:如果这个时候\(B\)串中没有\(1\)那么当然不用玩了
只要\(B\)中有\(1\)一定有解,因为实在不行你可以把每一个要变的位置都先转到那里去然后反转一下再转回来
那么现在。。来看怎么计算最小操作数(其实想法很暴力==)
首先我们要解决一个问题:最后\(A\)的每一位对的是\(B\)的哪一位(也就是那个下标的偏移量具体是多少),既然长度只有\(2000\)那当然是枚举啊(什么玩意==)
假设最后的偏移量是\(mv\),那么再怎么样我们肯定要把\(A\)转到那个位置去,所以如果说这转的中途有需要反转的位置可以对上一个\(1\)的话,就直接反转就好了,同时还有一些位置可能并不能对上一个\(1\),那么这些位置还需要额外往左边或者右边转一段距离反转,然后再转回来
假设我们现在已经知道了所有需要额外往左边或者右边移动的位置,以及他们具体往左边(或右边)移动多少,假设往左边移动最多的是\(mxl\),往右边移动最多的是\(mxr\),那么除去那个必须移动的偏移量以及反转的贡献以外,还需要\(2*(mxl+mxr)\)步,具体的话就是因为。。你要先往左边走\(mxl\)步,然后走偏移量,然后再往右边走\(mxr\)步,完了之后还要走回来
那么现在我们要先出这些位置往左边或右边走到第一个\(1\)所需要的操作数量,这个可以通过预处理得到比较简单就不再赘述
最后的问题就是。。怎么求出最小的\(2*(mxl+mxr)\):假设我们将每个位置往左走往右走的操作数量记成一个数对\((l,r)\),那么我们可以将这些数对先按照\(l\)排序,然后从大到小扫,对于每一个位置,计算当这个位置的\(l\)为\(mxl\)时的\(mxr\)应该是多少(具体就是。。边扫边记录扫过的\(r\)的\(max\)就好了,因为如果当前的位置要是\(mxl\),那么比它\(l\)大的位置只能往右走,而比它\(l\)小的位置怎么走都无所谓但是为了让\(mxr\)尽量小,所以我们当然希望往右走的位置尽量少)
然后。。总的时间复杂度就是\(O(n^2logn)\)了(虽然说里面扫是线性的但是需要排序)
mark:所以其实有些时候需要暴力一点的想法==
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define mp make_pair
#define Pr pair<int,int>
using namespace std;
const int N=6010,inf=1e9;
vector<Pr> rec;
char A[N],B[N];
int cnt1[N],L[N],R[N];
int n,m,ok,ans;
int Id(int x){return x+n;}
void prework(){
for (int i=1;i<=n;++i){
B[i+n]=B[i+n+n]=B[i];
if (A[i]!=B[i]) ok=false;
}
cnt1[0]=0;
for (int i=1;i<=n*3+1;++i)
cnt1[i]=cnt1[i-1]+(B[i]=='1');
int loc1=-1;
L[0]=-inf;
for (int i=1;i<=3*n;++i) L[i]=B[i]=='1'?i:L[i-1];
R[3*n+1]=inf;
for (int i=3*n;i>=1;--i) R[i]=B[i]=='1'?i:R[i+1];
}
int Abs(int x){return x<0?-x:x;}
void solve(){
if (ok){printf("0\n");return;}
if (cnt1[n]==0){printf("-1\n");return;}
int flip=0,l,r,mxr,tmpans;
ans=inf;
for (int mv=-n;mv<=n;++mv){
rec.clear(); flip=0; rec.push_back(mp(0,0));
for (int i=1;i<=n;++i){
if (A[i]==B[i+Id(mv)]) continue;
++flip;
l=i+Id(mv); r=Id(i);
if (l>r) swap(l,r);
if (cnt1[r]-cnt1[l-1]==0)
rec.push_back(mp(l-L[l],R[r]-r));
}
sort(rec.begin(),rec.end());
mxr=0; tmpans=inf;
for (int i=rec.size()-1;i>=0;--i){
if (rec[i].first!=-inf)
tmpans=min(tmpans,2*(rec[i].first+mxr));
mxr=max(mxr,rec[i].second);
if (mxr==inf) break;
}
if (tmpans!=inf)
ans=min(ans,tmpans+Abs(mv)+flip);
}
printf("%d\n",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%s",A+1); n=strlen(A+1);
scanf("%s",B+1); ok=true;
prework();
solve();
}