LGP5426题解
注意到交换两个不同的元素一定会改变逆序对的数量且变化量为 \(1\),所以对于答案有一个明显的下界是左边逆序对数量和右边逆序对数量之差的绝对值。
但是有的元素会跨越中点,所以考虑枚举有多少个 \(0,1\) 跨过了中点,能够计算出此时左边和右边分别的逆序对数量。
于是像上述那样处理一下,枚举跨过中点的元素,然后算逆序对之差的绝对值即可。复杂度可以做到 \(O(n)\)。
注意计算逆序对的时候的细节。
#include<cstdio>
#include<cctype>
namespace SOLVE{
typedef long long ll;
const int M=2e5+5;
int n,a[M];ll lans,rans;int l1,l2,t1[M],c1[M],t2[M],c2[M];
inline void swap(int&a,int&b){
int c=a;a=b;b=c;
}
inline ll abs(const ll a){
return a>0?a:-a;
}
inline ll min(const ll&a,const ll&b){
return a>b?b:a;
}
inline ll calc(const int&L,const int&R){
ll ans(0);int sum(0);for(int i=L;i<=R;++i)a[i]?++sum:ans+=sum;return ans;
}
inline int read(){
int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline ll Solve1(){
ll ans(abs(lans-rans)),ls(0),rs(0),s1(0),s2(0);l1=l2=0;
for(int i=n;i>=1;--i)if(!a[i])++l1,t1[l1]=i;for(int i=1;i<=l1;++i)c1[i]=t1[i]-1-(l1-i);
for(int i=n+1;i<=n*2;++i)if(a[i])++l2,t2[l2]=i;for(int i=1;i<=l2;++i)c2[i]=2*n-t2[i]-(l2-i);
for(int i=1;i<=l1&&i<=l2;++i){
ls+=c1[i],rs+=c2[i];s1+=n-i+1-t1[i];s2+=t2[i]-n-i;
ans=min(ans,abs((lans-ls)-(rans-rs))+s1+s2+1ll*i*i);
}
return ans;
}
inline ll Solve2(){
ll ans(abs(lans-rans)),ls(0),rs(0),s1(0),s2(0);l1=l2=0;
for(int i=n;i>=1;--i)if(a[i])++l1,t1[l1]=i;for(int i=1;i<=l1;++i)c1[i]=n-t1[i]-(i-1);
for(int i=n+1;i<=n*2;++i)if(!a[i])++l2,t2[l2]=i;for(int i=1;i<=l2;++i)c2[i]=t2[i]-n-1-(i-1);
for(int i=1;i<=l1&&i<=l2;++i){
ls+=c1[i],rs+=c2[i];s1+=n-i+1-t1[i];s2+=t2[i]-n-i;
ans=min(ans,abs((lans-ls+1ll*i*(l1-i))-(rans-rs+1ll*i*(l2-i)))+s1+s2+1ll*i*i);
}
return ans;
}
inline void main(){
n=read();for(int i=1;i<=n*2;++i)a[i]=read();lans=calc(1,n);rans=calc(n+1,n*2);
ll c1,c2;c1=Solve1();c2=Solve2();printf("%lld",c1>c2?c2:c1);
}
}
signed main(){
SOLVE::main();
}