P1966 [NOIP2013 提高组] 火柴排队 题解

文章介绍了如何通过分解和排序两个数列,利用逆序对的概念来计算交换次数,使两数列元素差平方和最小。作者给出了C++代码实现,涉及了数组操作、排序和合并排序等技术。
摘要由CSDN通过智能技术生成

题目

简洁化题意

给定两个长度为 n n n 的数列 a , b a,b a,b,然后交换数列里面相邻两个元素,使得
∑ i = 1 n ( a i − b i ) 2 \sum_{i=1}^{n} (a_i - b_i)^2 i=1n(aibi)2
最小,求交换次数。

思路

我们可以分解一下这个式子,得到:

∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 + ∑ i = 1 n a i × b i × 2 \sum_{i=1}^{n}{a_i} ^ 2 + \sum_{i = 1}^{n}{b_i}^2 + \sum_{i=1}^{n}a_i \times b_i \times 2 i=1nai2+i=1nbi2+i=1nai×bi×2

由此可见,前面两项都是定值,就只有第三项是变化的,又因为让差小就可以让总体小,所以我们尽量让数列里面的对应元素变小。

我们将 a a a 数组里的数列标上号,具体标法如下:

首先,我们求出 a , b a,b a,b 数组里每一个元素是数列里第几大的数,将 a a a 数组里第 1 1 1 大的与 b b b 数组里第 1 1 1 大的配对,将 a a a 数组里第 2 2 2 大的与 b b b 数组里第 2 2 2 大的配对……那么 a a a 数组里面的每一个数的号码就是与之配对的数在 b b b 数组里面的下标,在这个号码上求一遍逆序对即可。

原理:

我们可以将 b b b 数组看做一个 1 , 2 , 3 , 4 ⋯ n 1,2,3,4\cdots n 1,2,3,4n 的排列, a a a 数组里元素按标号排序后就是和 b b b 数组里的元素一一配对的。而要求交换的次数就是跑一边逆序对。

所以这个题也不难,就是要实现的东西有点多。

AC Code:

#include <bits/stdc++.h>
using namespace std;
int n;
struct node{
	int id, val;
};
node b[100100], a[100100];
bool cmp(node a, node b) {
	return a.val < b.val;
}
node a1[100100], b1[100100];
int idx[100100];
int f[100100];
long long ans;
int s1[1000010];
void mergesort(int a[], int l, int r) {
	if (l >= r) return ;
	int mid = (l + r) / 2;
	mergesort(a, l, mid);
	mergesort(a, mid + 1, r);
	int i1 = l, i2 = mid + 1;
	int i = l;
	while (i1 <= mid && i2 <= r) {
		if (a[i1] <= a[i2]) {
			s1[i] = a[i1];
			i1++;
			i++;
		} else {
			s1[i] = a[i2];
			ans += (mid - i1 + 1);
			ans %= 99999997;
			i2++;
			i++;
		}
	}
	while (i1 <= mid) {
		s1[i] = a[i1];
		i1++;
		i++;
	}
	while (i2 <= r) {
		s1[i] = a[i2];
		i2++;
		i++;
	}
	for (int ii = l; ii <= r; ii++) {
		a[ii] = s1[ii];
	}
}
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++) {
		scanf("%d", &a[i].val);
		a[i].id = i;
		a1[i].val = a[i].val;
	}
	for (int i = 1; i <= n; i ++) {
		scanf("%d", &b[i].val);
		b[i].id = i;
		b1[i].val = b[i].val;
	}
	sort (a + 1, a + n + 1, cmp);
	sort (b + 1, b + n + 1, cmp);
	for (int i = 1; i <= n; i ++) a1[a[i].id].id = i;
	for (int i = 1; i <= n; i ++) b1[b[i].id].id = i;
	for (int i = 1; i <= n; i ++) {
		idx[b1[i].id] = i;
	}
	for (int i = 1; i <= n; i ++) {
		f[i] = idx[a1[i].id];
	}
	mergesort(f, 1, n);
	cout << ans;
}
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值