火柴排队
题目描述
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:∑(ai−bi)^2
其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 10^8-3 取模的结果。
输入格式
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
输出格式
一个整数,表示最少交换次数对 10^8-3 取模的结果。
首先要推这个式子 ∑(ai−bi)^2=∑(ai ^2+bi ^2-2aibi) 可以发现ai ^2+bi ^2总和是不变,只要找到ai*bi的最大值;
可以发现,当ai和bi都是升序排列的时候,ai*bi是最大的;
所以可以把ai和bi都离散化以后,以ai为序列当做升序模板,把bi的排列变为ai的排列顺序;
可以想到这里就是求逆序对了;
很多求解法,我用的是权值线段树;
代码:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100010;
const int M=2000100;
const LL mod=1e8-3;
int n,a[N],a1[N],b[N],b1[N],sum,pos[N];
LL ans;
struct Node{
int l,r,w;
}tr[N*4];
void build(int l,int r,int k){
tr[k].l=l,tr[k].r=r;
if(l==r){
tr[k].w=0;
return;
}
int d=(l+r)>>1;
build(l,d,ls);
build(d+1,r,rs);
tr[k].w=tr[ls].w+tr[rs].w;
}
void add(int p,int k){
if(tr[k].l==tr[k].r){
tr[k].w++;
return;
}
int d=(tr[k].l+tr[k].r)>>1;
if(p<=d) add(p,ls);
else add(p,rs);
tr[k].w=tr[ls].w+tr[rs].w;
}
void query(int ll,int rr,int k){
if(tr[k].l>=ll&&tr[k].r<=rr){
sum+=tr[k].w;
return;
}
int d=(tr[k].l+tr[k].r)>>1;
if(ll<=d) query(ll,rr,ls);
if(rr>d) query(ll,rr,rs);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a1[i]=a[i];
for(int i=1;i<=n;i++) scanf("%d",&b[i]),b1[i]=b[i];
sort(a1+1,a1+1+n),sort(b1+1,b1+1+n);
for(int i=1;i<=n;i++){
a[i]=lower_bound(a1+1,a1+n+1,a[i])-a1;
b[i]=lower_bound(b1+1,b1+n+1,b[i])-b1;
}
for(int i=1;i<=n;i++) pos[a[i]]=i;
build(1,n,1);
for(int i=1;i<=n;i++){
sum=0;
query(pos[b[i]]+1,n,1);
(ans+=sum)%=mod;
add(pos[b[i]],1);
}
cout<<ans<<endl;
return 0;
}