火柴排队

题目描述

涵涵有两盒火柴,每盒装有 nn 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: \sum (a_i-b_i)^2∑(ai​−bi​)2

其中 a_iai​ 表示第一列火柴中第 ii 个火柴的高度, b_ibi​ 表示第二列火柴中第 ii 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,99799,999,997 取模的结果。

输入输出格式

输入格式:

共三行,第一行包含一个整数 nn ,表示每盒中火柴的数目。

第二行有 nn 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

第三行有 nn 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

 

输出格式:

一个整数,表示最少交换次数对 99,999,99799,999,997 取模的结果。

 

输入输出样例

输入样例#1:

4
2 3 1 4
3 2 1 4

输出样例#1:

1

输入样例#2:

4
1 3 4 2
1 7 2 4

输出样例#2:

2

说明

【输入输出样例说明1】

最小距离是 00 ,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。

【输入输出样例说明2】

最小距离是 10 ,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。

题解:

我们的目的是让ai - bi尽可能小,也就是说要a中的第k大减去b中的第k大
很容易想到a和b按高度从大到小排序,得到的这个序列一定是最优的
接下来求交换次数。说到“交换次数”很容易联想到逆序对,但是这里借助离散化的思想
我们想象把排好序的数组“还原”回去,就是说让所有的f(i)都等于i
在f序列中的逆序对个数就是我们的交换次数
然后用归并排序求出f的逆序对即为所求

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#define MAXA 100005
#define MOD 99999997 
using namespace std;
typedef long long LL;
struct Rx {
	int Height;
	int Idx;
}a[MAXA],b[MAXA];
bool cmp(Rx a,Rx b) {
	return a.Height < b.Height;
}
int n,Ans,f[MAXA],temp[MAXA];

void Merge_sort(int Left,int Right) {
	if(Left >= Right)
	   return;
	int Mid = (Left + Right) >> 1;
	Merge_sort(Left,Mid);
	Merge_sort(Mid + 1,Right);
	int i = Left,j = Mid + 1,k = Left;
	while(i <= Mid && j <= Right) {
		if(f[i] > f[j]) {
			temp[k++] = f[j++];
			Ans = (Ans + Mid - i + 1) % MOD;
		}
		else temp[k++] = f[i++];
	}
	while(i <= Mid)
	   temp[k++] = f[i++];
	while(j <= Right)
	   temp[k++] = f[j++];
	for(int i=Left;i<=Right;i++)
	   f[i] = temp[i];
}
int main() {
	//freopen("match.in","r",stdin);
	//freopen("match.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i].Height);
		a[i].Idx = i;
	}
	for(int i=1;i<=n;i++) {
		scanf("%d",&b[i].Height);
		b[i].Idx = i;
	}
	
	sort(a + 1,a + n + 1,cmp);
	sort(b + 1,b + n + 1,cmp);
	
	for(int i=1;i<=n;i++)
	    f[a[i].Idx] = b[i].Idx;
	
	Merge_sort(1,n);
	printf("%d",Ans % MOD);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值