洛谷 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;
}
本文来自博客园,作者:B_F_S,转载请注明原文链接:https://www.cnblogs.com/Na2SO4/p/solution-P5426.html