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号