首先若a,b线段交叉且A[a]< A[b],则B[a]>B[b],若我们给A序列强制从小到大的标号,转换B序列保证符合原题。则线段交叉的对数,就是B序列中逆序对的个数。我们可以用树状数组nlogn求出。然后考虑循环移位,现在只考虑对B序列做循环移位(A序列同理,反过来做即可。),实质上就是对转换后的B序列做循环移位,我们每次只把最后一位挪到最前面,挪n-1次即可得到所有循环移位后的结果。考虑把最后一位x挪到第一位,对逆序对个数的影响是什么。因为操作之后x在第一位,所以所有比x小的都在他后面,构成了新的x-1个逆序对。原来比x大的现在都不再和x构成逆序对,所以少了(n-x)个以前的逆序对。而其他的位置没有变化。因此操作过后逆序对数的变化为x-1-(n-x)。这样我们只需用树状数组求出第一次的逆序对个数,以后的逆序对个数都可以O(1)得到,我们记录最小值即可。
tips:转A和转B得到的序列是不同的,必须做两次。
#include <cstdio>
#include <cstring>
#define N 100010
#define ll long long
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll min(ll x,ll y){return x<y?x:y;}
int n,a[N],b[N],c[N];
ll ans=0;
inline int lowbit(int x){return x&(-x);}
inline void insert(int x){
for(;x<=n;x+=lowbit(x)) c[x]+=1;
}
inline ll query(int x){
ll res=0;
for(;x>0;x-=lowbit(x)) res+=c[x];
return res;
}
int main(){
// freopen("a.in","r",stdin);
n=read();
for(int i=1;i<=n;++i){
int x=read();b[x]=i;
}
for(int i=1;i<=n;++i){int x=read();a[i]=b[x];}
ll res=0;
for(int i=n;i>=1;--i){
res+=query(a[i]);insert(a[i]);
}
ans=res;
for(int i=n;i>=2;--i) res+=a[i]-1-(n-a[i]),ans=min(ans,res);
for(int i=1;i<=n;++i) b[a[i]]=i;
res=0;memset(c,0,sizeof(c));//注意清零
for(int i=n;i>=1;--i){
res+=query(b[i]);insert(b[i]);
}
ans=min(ans,res);
for(int i=n;i>=2;--i) res+=b[i]-1-(n-b[i]),ans=min(ans,res);
printf("%lld\n",ans);
return 0;
}