P5426 [USACO19OPEN]Balancing Inversions 枚举模拟

本文解决了一个关于两组01序列的逆序对匹配问题,通过枚举和交换操作,寻找使左右两边逆序对数量相等所需的最小交换次数。介绍了一种有效的算法实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接: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

*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值