题目链接:https://www.luogu.org/problemnew/show/P5426
题意:
给了你2*n的01串,现在你可以将相邻的两个值进行交换,问你最少交换几次可以使得左边n个数中的逆序对等于右边的逆序对,这里的逆序对的其实是i<j且a[i]=1,a[j]=0.
做法:
其实就是暴力进行枚举,如果不变,那么答案就是前面的对数减去后面的对数,假设我们要将左边的1放到右边的0处,那么我们就要找到从中间开始的向左的第一个1和向右的第一个0(因为这样做的话对答案的贡献可以控制到最小)。然后进行交换,交换之后对前面对数x和后面对数y的影响分别是,假设前面的1位置为pos,到了后面,那么x要减去a数组中pos到n中的0的个数,加上1到pos上的1的个数。假设后面的0位置为pos,到了前面,那么y要减去b数组中1到pos中的1的个数,加上pos到n上的0的个数。 如果把左边的1放到右边也是类似的,就不多讲了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100005;
ll lefone0[maxn],rigzero1[maxn];
int a[maxn],b[maxn],n;
ll ans;
ll abs(ll a,ll b){
if(a<b) swap(a,b);
return a-b;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) lefone0[i]=lefone0[i-1]+a[i];
for(int i=n;i>=1;i--) rigzero1[i]=rigzero1[i+1]+(b[i]==0);
ll tmp=0,pre=0,lat=0;
for(int i=1;i<=n;i++) {
if(a[i]==1) tmp++;
else pre+=tmp;
}
tmp=0;
for(int i=1;i<=n;i++) {
if(b[i]==1) tmp++;
else lat+=tmp;
}
ans=abs(pre-lat);
if(ans==0) return 0*printf("0\n");
int pl=n,pr=1;
ll add=0,tmppre=pre,tmplat=lat;
while(1){
while(pl>=1&&a[pl]!=1) pl--;
while(pr<=n&&b[pr]!=0) pr++;
if(pl<1||pr>n) break;
add+=n+pr-pl;
tmppre=tmppre-(n-pl)+lefone0[pl-1],tmplat=tmplat-(pr-1)+rigzero1[pr+1];
ans=min(ans,abs(tmppre-tmplat)+add);
pl--,pr++;
}
tmppre=pre,tmplat=lat;
pl=n; pr=1; add=0;
while(1){
while(pl>=1&&a[pl]!=0) pl--;
while(pr<=n&&b[pr]!=1) pr++;
if(pl<1||pr>n) break;
add+=n+pr-pl;
tmppre=tmppre-lefone0[pl],tmplat=tmplat-rigzero1[pr];
ans=min(ans,abs(tmppre-tmplat)+add);
pl--,pr++;
}
printf("%lld\n",ans);
return 0;
}
/*
10
1 0 1 1 0 1 0 0 1 0 0 0 0 0 1 1 1 0 1 0
1 1 1 0 0 0 1 1 0 1 0 1
*/