[NOIP2013提高组]火柴排队

题目:洛谷P1966、Vijos P1842、codevs3286。

题目大意:有两排火柴,每根都有一个高度。设a、b分别表示两排火柴的高度,现在要令$sum(a_i-b_i)^2$最小。现两排火柴已经排成一个序列,求最少交换多少次能满足条件。

解题思路:首先,只有当最大的对应最大的,次大的对应次大的,以此类推,得到的答案才可能最小。

然后我们发现,如此分析后,此题的答案与火柴实际长度无关,只要按照原来的大小顺序即可,且只需考虑对一排火柴进行交换即可。

因此我们对两组数据分别离散,然后通过第一组数据再对第二组数据再离散一遍即可。

最后其实就是求逆序对了,归并排序即可。

时间复杂度$O(nlog _2 n)$。

既然写了归并,还用什么sort(*^__^*)

C++ Code:

#include<cstdio>
#include<cstring>
using namespace std;
int n,a[100005],ans,b[100005],c[100005],ys[100005],p[100005];
void merge(int* a,int l,int r){
	int i=l,mid=l+r>>1,k=l;
	int j=mid+1;
	while(i<=mid&&j<=r){
		if(a[i]>a[j]){
			ans=(ans+mid-i+1)%99999997;
			c[k++]=a[j++];
		}
		else
		c[k++]=a[i++];
	}
	while(i<=mid)c[k++]=a[i++];
	while(j<=r)c[k++]=a[j++];
	for(i=l;i<=r;++i)a[i]=c[i];
}
void mergesort(int* a,int l,int r){
	if(l!=r){
		int m=l+r>>1;
		mergesort(a,l,m);
		mergesort(a,m+1,r);
		merge(a,l,r);
	}
}
void merge_sort(int* a,int l,int r){
	ans=0;
	mergesort(a,l,r);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	memcpy(p,a,sizeof p);
	merge_sort(p,1,n);
	for(int i=1;i<=n;++i)
	ys[p[i]]=i;
	for(int i=1;i<=n;++i)a[i]=ys[a[i]];
	for(int i=1;i<=n;++i)scanf("%d",&b[i]);
	memcpy(p,b,sizeof p);
	merge_sort(p,1,n);
	for(int i=1;i<=n;++i)
	ys[p[i]]=i;
	for(int i=1;i<=n;++i)b[i]=ys[b[i]];
	for(int i=1;i<=n;++i)
	ys[a[i]]=i;
	for(int i=1;i<=n;++i)b[i]=ys[b[i]];
	merge_sort(b,1,n);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Mrsrz/p/7642765.html