把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷1966】火柴排队(逆序对)

点此看题面

大致题意:\(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\),此时只有两种匹配方式:

  1. \(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\)
  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) \]

\[\because a_1<a_2,b_1<b_2,\therefore S_1-S_2<0,\therefore 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
}
posted @ 2019-05-27 20:30  TheLostWeak  阅读(154)  评论(0编辑  收藏  举报