这里是题目:题目传送门
这道题,乍一看上去,其实根本没有什么思路。
首先,先来一波玄学证明:
∑(ai−bi)2=∑(ai2+bi^2)-2∑(ai+bi)
首先,前面的平方的和是固定不变的,也就是说,要使两排火柴之间的距离尽量小,就要使2∑(ai+bi)尽量大,也就是说,大的a[i]配大的b[i],小的a[i]配小的b[i],这又是为什么捏?
我们拿2个数来看,假设第一排中a<b,第二排中c<d,那么ac+b*d一定是最大的。
ac+bd-(ad+bc)=(a-b)(c-d)>0
同理可以推到每一排k个数也是这样的(问我怎么证明?以k为例子,首先,我们可以得到每排前面k-1个排出的最大值,之后将其看作一个整体,与每一排的第k个数配对,这就是一个每排就只有两个数的问题了,递推下去就可以证明结论成立了),其实就是用到了数学归纳法。
得到了这个结论之后,要想使两排大小的排序顺序相同,依然不是一道简单的题目。
首先,注意到只需要看顺序问题,于是想到先要运用到拆分的思想,不管原来的数是多少。只关心在这一排中是第几大的。于是我们用rank[i]表示第i大的数在原数组里面是第几个,之后,我们就得到了两行的排序情况。
之后,我们又怎么通过最少次数的操作,使得两行的排序情况相同捏?考虑第二排的情况,我们看第二排的第i个在第一排中所对应的位置,然后与自己本身的rank值进行比较,得到最小的交换次数就是两个数组之间的逆序对的数量
接下来,又是一波玄学证明:
为什么最小的交换次数就是两个数对的逆序对的数量捏?首先,我们要证明一件事请,就是,假如还有逆序对存在,那么就必定会有两个相邻的点是逆序对:
假设相邻的两个点之间都不存在逆序对,那么对于所有的i来讲,a[i]<a[i+1]。因此我们可以得到该序列一定是单调递增的,这时候,是没有逆序对的。
所以,我们一定可以找到两个相邻的点,这两点之间就是一个逆序对。点击一次,就将这两个数调换位置。之后再来考虑交换完这两个相邻的数之后,逆序对减少的情况。
假设a[i]与a[i+1]交换之前,a[i]与前面k1个数形成了逆序对,a[i]与后面k2个数形成了逆序对,交换完之后,后面的逆序对减少了1(就是a[i],a[i+1])这一对,同理,a[i+1]也少了a[i],a[i+1]这一对,但是由于二者是相同的所以总共减少了一组逆序对。
之后,还有一个问题,就是在统计有多少组逆序对的时候,不要枚举每一个点,然后再求出相对应的逆序对,这样的时间法度是O(n^2)的,最终会超时,我们可以使用归并排序,在排序的过程中,和并一次就统计一下逆序对的个数,最后再加起来,这样的时间法度是O(nlog(n))级别的。
代码如下:
#include<iostream>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxN=100100;
const int mod=99999997;
int n,ans;
int a[maxN][2],rank[maxN][2];