代码源:814、排队

294 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路 logo.png

题目描述

这是4月22日代码源div2的每日一题。

排队 - 题目 - Daimayuan Online Judge

古老的星球上有这样一群人,他们每年都会参加盛大的周年庆。在进入场地之前所有人在入口排成两队,每队人数都是 n 人,第一队第 i 人身高为 ai,第二队第 i 人身高为 bi。

人们在排队时,喜欢跟另一队相同位置的伙伴亲切地交谈。但是如果这两人身高相差太多,他们会感到有些尴尬,两人的尴尬指数 gi 与身高差呈这样一种关系:gi=(ai−bi)2。

为了尽可能减轻尴尬,每队的人都可以与前后相邻者交换位置,次数不限,但只能在同队范围内交换。问最少需要交换多少次位置,可以使得总体尴尬度 ∑(ai−bi)2 最小。如果答案太大,请输出这个最少交换次数对 108−7 取模的结果。

保证每队人的身高两两不同。

输入格式

输入共三行。第一行是一个正整数 n,表示每队的人数。 第二行有 n 个整数,每两个整数用一个空格隔开,表示第一队人们的身高。 第三行有 n 个整数,每两个整数用一个空格隔开,表示第二队人们的身高。

输出格式

一个整数,表示最少的交换次数。

数据范围

1≤n≤10^5,0≤ai,bi<2^31,保证每队人们的身高各不相同

输入样例

4
2 3 1 4
3 2 1 4

输出样例

1

样例解释

只需交换第一队前两人,或者交换第二队前两人。这样人们都跟与自己身高相同的伙伴交谈,总体尴尬度为0,即最小。

问题解析

首先,我们要(a-b)^2最小,我们把式子展开得到(a^2-2ab-b^2),然后我们应该清楚,不管a和b位置如何,最后的结果a^2和b^2这两种情况是改不了的,所以我们只能从2ab上下手,然后通过这个式子我们也能看出,要想(a-b)^2最小,那么2ab应该要最大。

此时问题初步变成了,如何排序能让2ab最大。这里涉及到一个知识(我觉着挺神奇),就是说如果两个数组相乘,有序的数组相乘会大于无序的数组相乘。(关于证明感兴趣的可以去上网学习一下),所以我们现在要做的就是把一个数组对于另一个数组相对有序:两边数组第一大的在同一个位置,第三大的在同一个位置,第二大的在同一个位置(不是说数组必须有序,而是相对有序)。

现在题目最终变成了,每次可以交换两个相邻的数,问两个数组变的相对有序,步数最少是几步。这一步就相当于是求逆序对了,我们固定一个数组的顺序,仅改变另一个数组的值。那我们就根据固定的数组为准来离散化另一个数组,然后求出这个数组的逆序对数量,至于求逆序对用线段树或者逆序对都是可以的。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1e5 + 50, MOD = 1e8 - 7;
ll f[4 * N];

void revise(int k, int l, int r, int x)
{
	if (l == r)
	{
		f[k] += 1;
		return;
	}
	int m = (l + r) / 2;
	if (x <= m)revise(k + k, l, m, x);
	else revise(k + k + 1, m + 1, r, x);
	f[k] = (f[k + k] + f[k + k + 1])%MOD;
}

ll calc(int k, int l, int r, int x, int y)
{
	if (l == x && y == r)
	{
		return f[k];
	}
	int m = (l +r) / 2;
	if (y <= m)return calc(k + k, l, m, x, y);
	else
		if (x > m)return calc(k + k + 1, m + 1, r, x, y);
		else return (calc(k + k, l, m, x, m) + calc(k + k + 1, m + 1, r, m + 1, y))%MOD;
}

int main() {
	int n;
	cin >> n;
	vector<PII>a(n), b(n);
	for (int i = 0; i < n; i++)
	{
		cin >> a[i].first;
		a[i].second = i;
	}
	for (int i = 0; i < n; i++)
	{
		cin >> b[i].first;
		b[i].second = i;
	}
	sort(a.begin(), a.end());
	sort(b.begin(), b.end());
	vector<int>v(n);
	for (int i = 0; i < n; i++)
	{
		v[a[i].second] = b[i].second + 1;
	}
	ll res = 0;
	for (int i = n-1; i >= 0; i--)
	{
		if (v[i] != 1)res = (res + calc(1, 1, n, 1, v[i] - 1))%MOD;
		revise(1, 1, n, v[i]);
	}
	cout << res << endl;
	
	return 0;
}