洛谷 P1966:[NOIP 2013 提高组] 火柴排队 ← 离散化(lower_bound 或 STL map)+ 归并排序 + 逆序对

​​【题目来源】
https://www.luogu.com.cn/problem/P1966

【题目描述】
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:∑(aᵢ−bᵢ)²。
其中 aᵢ 表示第一列火柴中第 i 个火柴的高度,bᵢ 表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 10⁸−3 取模的结果。

【输入格式】
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

【输出格式】
一个整数,表示最少交换次数对 10⁸−3 取模的结果。

【输入样例】
4
1 3 4 2
1 7 2 4

【输出样例】
2

【数据范围】
对于10%的数据,1≤n≤10;
对于30%的数据,1≤n≤100;
对于60%的数据,1≤n≤10^3;
对于100%的数据,1≤n≤10^5,0≤ai,bi<2^31且对于任意1≤i<j≤n,ai≠aj,bi≠bj。

【算法分析】
● 离散化‌是一种将一组不连续、无规律或取值范围很大的数据转换成一个从小到大的整数序列的方法,使得我们可以使用数组下标来处理原本无法直接处理的值。通过离散化,可以将无限空间中的有限个体映射到有限的空间中去,从而提高算法的时空效率‌。
● 将稀疏分布的大范围数据压缩映射到紧凑连续空间是离散化的典型操作。例如,将数值差距大的非连续点(如 1, 999, 100000)映射为连续的数组下标(如 1, 2, 3),同时保持数据相对大小关系不变。
● 离散化是一种数据处理的技巧。可以进行离散化的数据有大整数、浮点数、字符串等。
● 简单而言,常用的离散化算法步骤为“排序、去重、映射”。在 C++ 中,离散化常用 lower_bound() 或 STL map 实现。个人认为,利用 STL map 实现的离散化代码,最是简单易懂。

【算法代码:STL map 离散化】

#include <bits/stdc++.h>
using namespace std;

const int MOD=1e8-3;
const int N=1e5+5;
int a[N],b[N];
int t[N]; //临时数组
int c[N]; //逆序对数组
long long ans;

void discretize(int a[], int n) {
	map<int,int> mp;
	for(int i=1; i<=n; i++) mp[a[i]]=0;
	int rk=0;
	for(auto& x:mp) x.second=++rk;
	for(int i=1; i<=n; i++) a[i]=mp[a[i]];
}

void merge_sort(int le,int ri) {
	if(le==ri) return;
	int mid=(le+ri)>>1;
	merge_sort(le,mid);
	merge_sort(mid+1,ri);

	int p=le;
	int i=le,j=mid+1;
	while(i<=mid && j<=ri) {
		if(c[i]<=c[j]) t[p++]=c[i++];
		else {
			t[p++]=c[j++];
			ans+=mid-i+1; //统计逆序对
		}
	}
	while(i<=mid) t[p++]=c[i++];
	while(j<=ri) t[p++]=c[j++];

	for(int k=le; k<=ri; k++) c[k]=t[k];
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n;
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i];
	for(int i=1; i<=n; i++) cin>>b[i];

	discretize(a,n);
	discretize(b,n);

	//构造c[]数组:a[]的排名 --> b[]的位置
	for(int i=1; i<=n; i++) t[b[i]]=i;
	for(int i=1; i<=n; i++) c[i]=t[a[i]];;

	merge_sort(1,n);
	cout<<ans%MOD<<endl;

	return 0;
}

/*
in:
4
1 3 4 2
1 7 2 4

out:
2
*/

【算法代码:lower_bound 离散化】

#include <bits/stdc++.h>
using namespace std;

const int MOD=1e8-3;
const int N=1e5+5;
int a[N],b[N];
int t[N]; //临时数组
int c[N]; //逆序对数组
long long ans;

void merge_sort(int le,int ri) {
	if(le==ri) return;
	int mid=(le+ri)>>1;
	merge_sort(le,mid);
	merge_sort(mid+1,ri);

	int p=le;
	int i=le,j=mid+1;
	while(i<=mid && j<=ri) {
		if(c[i]<=c[j]) t[p++]=c[i++];
		else {
			t[p++]=c[j++];
			ans+=mid-i+1; //统计逆序对
		}
	}
	while(i<=mid) t[p++]=c[i++];
	while(j<=ri) t[p++]=c[j++];

	for(int k=le; k<=ri; k++) c[k]=t[k];
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n;
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i];
	for(int i=1; i<=n; i++) cin>>b[i];

	//离散化数组a[]
	for(int i=1; i<=n; i++) t[i]=a[i];
	sort(t+1,t+n+1);
	for(int i=1; i<=n; i++) {
		a[i]=lower_bound(t+1,t+1+n,a[i])-t;
	}

	//离散化数组b[]
	for(int i=1; i<=n; i++) t[i]=b[i];
	sort(t+1,t+n+1);
	for(int i=1; i<=n; i++) {
		b[i]=lower_bound(t+1,t+1+n,b[i])-t;
	}

	//构造c[]数组:a[]的排名 --> b[]的位置
	for(int i=1; i<=n; i++) t[b[i]]=i;
	for(int i=1; i<=n; i++) c[i]=t[a[i]];

	merge_sort(1,n);
	cout<<ans%MOD<<endl;

	return 0;
}

/*
in:
4
1 3 4 2
1 7 2 4

out:
2
*/



【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/145679752
https://blog.csdn.net/hnjzsyjyj/article/details/143275575
https://www.luogu.com.cn/problem/solution/P1966
 

​

posted @ 2026-05-06 15:31  Triwa  阅读(37)  评论(0)    收藏  举报