题目
简洁化题意
给定两个长度为
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=1∑n(ai−bi)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=1∑nai2+i=1∑nbi2+i=1∑nai×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,4⋯n 的排列, 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;
}