4877: 火柴排队 归并排序
描述
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:Σ(ai-bi)^2, i=1~n,其中ai表示第一列火柴中第i个火柴的高度,bi表示第二列火柴中第i个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
输入
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
输出
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
样例输入
4
2 3 1 4
3 2 1 4
样例输出
1
提示
对于 10%的数据, 1 ≤ n ≤ 10;
对于 30%的数据,1 ≤ n ≤ 100;
对于 60%的数据,1 ≤ n ≤ 1,000;
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 2^31-1
首先我们知道要使得原式最小,只有在两列数的第K大相互对应的时候最小(证明略)。
那么问题就转化成了:
把数列b的数字大小关系移动成数列a的大小关系所需要的最小移动次数。
如何做呢?举个例子:
3 2 4 1
2 4 1 3
现在要求最小移动次数是的数列b变为:3 2 4 1
那么说明原位置1要移动到2,2移动到3,3移动到4,4移动到1(这里的数字指位置)。对应到一个新数列中即为:
2 3 4 1
目标状态为:
1 2 3 4
那么答案就为让这个新序列有序的最小移动次数,于是就是裸的逆序对了,用树状数组或归并排序均可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+10,inf = 0x3f3f3f3f,mod = 99999997; struct node{ int bh,num; }; node a[N],b[N]; ll c[N],r[N]; ll n,sum; bool cmp(node a,node b) { return a.num < b.num; } void merge(int s,int mid,int t) { int i = s,k = s,j = mid+1; while(i<=mid && j<=t) { if(c[i]>c[j]) { r[k++] = c[j++]; sum += mid-i+1; } else r[k++] = c[i++]; } while(i<=mid) r[k++] = c[i++]; while(j<=t) r[k++] = c[j++]; for(int x = s;x <= t;x++)c[x] = r[x]; } void mergesort(int s,int t) { if(s<t) { int mid = (s+t)>>1; mergesort(s,mid); mergesort(mid+1,t); merge(s,mid,t); } } int main() { cin >> n; for(int i=1;i<=n;i++) { scanf("%d",&a[i].num); a[i].bh = i; } for(int i=1;i<=n;i++) { scanf("%d",&b[i].num); b[i].bh = i; } sort(a+1,a+1+n,cmp); sort(b+1,b+1+n,cmp); for(int i=1;i<=n;i++)c[b[i].bh] = a[i].bh; mergesort(1,n); cout << sum%mod << endl; return 0; }