洛谷 P5426 题解

题目传送门

题意概述

  • 给定一个长度为 \(2\times n\) 的布尔数列,求交换的最小次数,使得左右两个长度为 \(n\) 的数列的逆序对的数量相同。

思考

首先,我们令 \(\Delta=f(1,n)-f(n+1,2\times n)\), 其中 \(f(i,j)\) 表示这个数列下标在 \(i\)\(j\) 之间的这个子数列的逆序对数,那么答案就是使 \(\Delta=0\) 的最小次数。举个例子:

5
0 0 0 1 0 1 0 0 0 1

在左边 \(n\) 个数中,互相交换位置,改变的逆序对数 \(\delta=\pm 1\)

但是,在中间的 0 1 交换就会造成较大的变动。

具体的,左侧的所有 1 与第 \(n\) 号位置的 0 组成的逆序对消失了,同时第 \(n+1\) 号位的新的 1 与后面所有的 0 会产生新的逆序对。

所以,\(\Delta\) 改变的数量为:\(\text{Left1}+\text{Right1}-n=sum-n\)。其中 \(sum\) 为整个数列中 1 的个数,即数列元素之和。

但是,如果中间的两个元素一样怎么办?再看一个例子:

5
0 1 0 1 1 0 0 0 1 1

当然,最优方法显然并不涉及到中间两者的交换,仅供演示。

我们考虑将最靠近 \(n\) 的左侧第一个 0最靠近 \(n+1\) 的右侧第一个 1 分别换到 \(n\)\(n+1\) 两个位置,再进行一次交换。此时记录这次交换所需要的次数,以及交换后 \(\Delta\) 的值。

所以,这种交换的次数最多为左侧 0 的个数,即不超过 \(n\),可以直接进行枚举。

同样的,我们对于另一种操作,也进行差不多的枚举。

时间复杂度 \(\Theta(n)\),似乎很容易被 \(10^5\) 的数据范围骗到。

CODE

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=200005;
int x[N],y[N],z[N],n;
ll ans,ansL,ansR;
int L0[N],L1[N],R0[N],R1[N];
int L0siz=0,R1siz=0,R0siz=0,L1siz=0;
ll sum=0;
void merge_sort(int L,int R)
{
    ans=0;
    int zr=0;
    for(int i=R;i>=L;i--)
    {
        if(!x[i]) zr++;
        else ans+=zr;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=2*n;i++)
	{
		scanf("%d",&x[i]);
		sum+=x[i];
	}
    merge_sort(1,n);
    ansL=ans;
    merge_sort(n+1,2*n);
    ansR=ans;
    ll Dt=ansL-ansR;
    if(Dt==0)
    {
        printf("0");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        if(x[i]==0) L0[++L0siz]=i;
        else L1[++L1siz]=i;
    }
    for(int i=2*n;i>=n+1;i--)
    {
        if(x[i]==1) R1[++R1siz]=i;
        else R0[++R0siz]=i;
    }
    ll ans=abs(Dt);
    ll step=0;
    while(L0siz>0&&R1siz>0)
    {
        int LL=L0[L0siz],RR=R1[R1siz];
        int tmp=(n-LL)-(RR-n-1)-(sum-n);
        Dt+=tmp;
        step+=(n-LL)+(RR-n-1)+1;
        L0siz--;
        R1siz--;
        ans=min(ans,abs(Dt)+step);
    }
    Dt=ansL-ansR;
	step=0;
	while(L1siz>0&&R0siz>0)
	{
	    int LL=L1[L1siz],RR=R0[R0siz];
	    int tmp=(n-LL)-(RR-n-1)-(sum-n);
	    Dt-=tmp;
	    step+=(n-LL)+(RR-n-1)+1;
	    L1siz--;
	    R0siz--;
	    ans=min(ans,abs(Dt)+step);
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2021-09-27 22:09  B_F_S  阅读(60)  评论(0编辑  收藏  举报