2025.7.14 归并排序
2025.7.14 归并排序(merge_sort)
归并排序是高效的基于比较的稳定排序算法。
还可以求逆序对。
思想
对于两个有序的数组 \(a[n]\) 和 \(b[m]\) ,可以合并到一个数组 \(c[n+m]\) 中。
用双指针实现,每次往 \(c\) 数组中放入最大(最小)的元素。
时间复杂度为 \(O(n\log n)\)
我们规定数组 \(a\) 在前,数组 \(b\) 在后,从小到大排序。
如果 a[i]>b[j] ,因为数组 \(a\) 已经有序,所以 b[j]<a[i]<a[i+1]<...<a[n] ,对于 \(b[j]\) 来说,逆序对个数为 \(n-i+1\) 。
实现
void merge_sort(int l,int r){
    if(l==r){
        return ;
    }
    int mid=(l+r)/2;
    merge_sort(l,mid),merge_sort(mid+1,r);//两个数组a[i]~a[mid]和a[mid+1]~a[r]
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(a[i]<a[j]){
            t[k++]=a[i++];
        }
        else{
            t[k++]=a[j++];
            ans+=mid-i+1;//逆序对个数
        }
    }
    while(i<=mid){
        t[k++]=a[i++];
    }
    while(j<=r){
        t[k++]=a[j++];
    }
    for(int i=l;i<=r;i++){
        a[i]=t[i];
    }
}
例题P1966
题目描述
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:\(\sum(a_i−b_i)^2\) 。
其中 \(a_i\) 表示第一列火柴中第 \(i\) 个火柴的高度,\(b_i\) 表示第二列火柴中第 \(i\) 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 \(10^8−3\) 取模的结果。
[!NOTE]
对于 \(100\%\) 的数据, \(1\leq n\leq 10^5,0\leq a_i,b_i<2^{31}\) 且对于任意 \(1\leq i<j\leq n,a_i\neq a_j,b_i\neq b_j\) 。
思路
先来解决一个数学问题:
\(\sum(a_i-b_i)^2=\sum a_i^2+\sum b_i^2-2*\sum a_ib_i\)
因为 \(\sum a_i^2,\sum b_i^2\) 都为定值,而距离尽量小,所以 \(\sum a_ib_i\) 尽量大。
由排序不等式可知:
设 \(a_1<a_2<...<a_n\), \(b_1<b_2<...<b_n\) ,则 \(\sum a_ib_i>=\sum a_ib_{j_i}\) 。
其中 \(j_i\) 是一个 \(n\) 的排列。
所以最小距离的成立条件即为:数组 \(a\) 中第 \(i\) 大的数,对应数组 \(b\) 中第 \(i\) 大的数。
我们对每个数的具体大小不关心,只关心它在数组中与其他数的大小关系,所以对数组进行离散化处理。
再把数组的数进行标记,求另一个数组的逆序对个数即可。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N],b[N],t[N],l[N],mod=1e8-3;
long long ans;
void lsh(){//离散化
	vector<int> v1,v2;
	for(int i=1;i<=n;i++){
		v1.push_back(a[i]);
		v2.push_back(b[i]);
	}
	sort(v1.begin(),v1.end());
	sort(v2.begin(),v2.end());
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(v1.begin(),v1.end(),a[i])-v1.begin()+1;
		b[i]=lower_bound(v2.begin(),v2.end(),b[i])-v2.begin()+1;
	}
}
void merge(int l,int r){//归并排序求逆序对
	if(l==r){
		return ;
	}
	int mid=(l+r)/2;
	merge(l,mid),merge(mid+1,r);
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r){
		if(b[i]<b[j]){
			t[k++]=b[i++];
		}
		else{
			t[k++]=b[j++];
			ans+=mid-i+1;
			ans%=mod;
		}
	}
	while(i<=mid){
		t[k++]=b[i++];
	}
	while(j<=r){
		t[k++]=b[j++];
	}
	for(int i=l;i<=r;i++){
		b[i]=t[i];
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	lsh();
	for(int i=1;i<=n;i++){
		l[a[i]]=i;//标记出现位置
	}
	for(int i=1;i<=n;i++){
		b[i]=l[b[i]];
	}
	merge(1,n);
	cout<<ans;
	return 0;
}
完结撒花!!!
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号