大致题意: 有(a)和(b)两个数组,问你要将(b)进行多少次相邻两数交换操作,才能使得(sum_{i=1}^n(a_i-b_i)^2)最小。
转化题意
不难猜到,当(a)中第(i)小的数与(b)中第(i)小的数配对时,值最小。
证明以(n=2)为例((n>2)同理),设(a_1<a_2,b_1<b_2),此时只有两种匹配方式:
- (S_1=(a_1-b_1)^2+(a_2-b_2)^2=a_1^2+a_2^2+b_1^2+b_2^2-2a_1b_1-2a_2b_2)
- (S_2=(a_1-b_2)^2+(a_2-b_1)^2=a_1^2+a_2^2+b_1^2+b_2^2-2a_1b_2-2a_2b_1)
则用(S_1)减(S_2)得:
[S_1-S_2=-2(a_1b_1+a_2b_2-a_1b_2-a_2b_1)=-2(a_2-a_1)(b_2-b_1)
]
[ecause a_1<a_2,b_1<b_2, herefore S_1-S_2<0, herefore S_1<S_2.
]
原命题得证。
设(rb_i)为(b_i)是(b)序列中第几小的数,则我们可以得出一个序列(s),其中(s_i)表示(a)序列中第(rb_i)小的数的位置。
那么题意就变成了求要对(s)序列进行多少次冒泡排序操作,才可以使(s)有序。
逆序对
根据一个著名定理,冒泡排序操作次数即为逆序对个数。
因此我们直接逆序对做即可。
我采用的是归并排序,具体实现见代码。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 99999997
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,s[N+5];
struct data
{
int p,v;I data(CI x=0,CI y=0):p(x),v(y){}
I bool operator < (Con data& o) Con {return v^o.v?v<o.v:p<o.p;}
}a[N+5],b[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
class MergeSolver//归并排序
{
private:
int tot,p[N+5];
I void Merge(CI l,CI r)
{
if(l>=r) return;RI mid=l+r>>1;Merge(l,mid),Merge(mid+1,r);
RI i=l,j=mid+1,k=l;W(i<=mid&&j<=r)
s[i]<=s[j]?p[k++]=s[i++]:(p[k++]=s[j++],Inc(tot,mid-i+1));//注意此处统计逆序对
W(i<=mid) p[k++]=s[i++];W(j<=r) p[k++]=s[j++];
for(i=l;i<=r;++i) s[i]=p[i];
}
public:
I void Solve() {Merge(1,n),printf("%d",tot);}
}M;
int main()
{
RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i].v),a[i].p=i;sort(a+1,a+n+1);
for(i=1;i<=n;++i) F.read(b[i].v),b[i].p=i;sort(b+1,b+n+1);
for(i=1;i<=n;++i) s[b[i].p]=a[i].p;return M.Solve(),0;//求出序列s
}