NOIP 2013 提高组 火柴排队 题解
前置知识:置换与排列
在做本题前,最好先做过 洛谷P1908 逆序对
Solution
假设当前处在最优解,那么交换某列火柴中的任意相邻位置,两列火柴间的距离均变大。
不妨设交换 \(a_i\) 和 \(a_{i+1}\),则有
火柴高度值域过大,考虑先对其离散化。由于同一列火柴的高度互不相同,离散化后会得到两个 \(1 \sim n\) 的排列,结合上式知取最优解时的两个排列相同。
(通过排序不等式也可得出相同结论,最小化 \(\sum(a_i-b_i)^2\) 等价于最大化 \(\sum a_ib_i\))
引理 存在一种最优解,可通过只交换一列火柴得到。
证明 设 \(A\) 为第一列火柴的初始排列,\(B\) 为第二列火柴的初始排列,一种最优排列为 \(P\)。\(A \to P\) 使用 \(\alpha\) 步,\(B \to P\) 使用 \(\beta\) 步,则总共使用 \(\alpha + \beta\) 步到达最优解。显然,如果 \(A \to P\) 最少使用 \(\alpha\) 步,那么 \(P \to A\) 也最少使用 \(\alpha\) 步。 考虑只对第二列火柴交换:\(B \to P \to A\),共使用 \(\alpha + \beta\) 步,且达到了一种最优解(\(A=A\))。
由上述引理,可设 \(Q = A^{-1}B\),题目所求的最小交换次数等于 \(Q \to I\) 的最小交换次数,其中 \(I\) 是 \(1 \sim n\) 的自然排列。
结论 \(Q \to I\) 的最小交换次数 \(ans\) 等于 \(Q\) 的逆序对个数 \(q\)。
证明 一方面,持续交换 \(Q\) 中相邻逆序对可将 \(Q\) 变为 \(I\),每次交换使总逆序对个数恰好减一,于是我们构造出了一种交换次数等于 \(q\) 的交换方式,这说明 \(ans \le q\);另一方面,交换 \(Q\) 中任意相邻位置,总逆序对个数至多减一,这说明 \(ans \ge q\)。
由上述结论,求 \(Q\) 的逆序对个数 \(q\) 即可。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e8 - 3, MAXN = 1e5;
int n, a[MAXN + 2], b[MAXN + 2], t[MAXN + 2], ans;
void msort(int l, int r) {
if (l >= r) return;
int m = (l + r) >> 1;
msort(l, m);
msort(m + 1, r);
int i = m, j = r, k = r;
while (i >= l && j >= m + 1) {
while (j >= m + 1 && a[i] <= a[j])
t[k--] = a[j--];
if (j >= m + 1 && a[i] > a[j])
ans = (ans + j - m) % MOD, t[k--] = a[i--];
}
if (i < l)
while (k >= l)
t[k--] = a[j--];
while (++k <= r)
a[k] = t[k];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", a + i), t[i] = a[i];
sort(t + 1, t + n + 1);
for (int i = 1; i <= n; ++i)
a[i] = lower_bound(t + 1, t + n + 1, a[i]) - t;
for (int i = 1; i <= n; ++i)
scanf("%d", b + i), t[i] = b[i];
sort(t + 1, t + n + 1);
for (int i = 1; i <= n; ++i)
b[i] = lower_bound(t + 1, t + n + 1, b[i]) - t;
for (int i = 1; i <= n; ++i)
t[a[i]] = i;
for (int i = 1; i <= n; ++i)
a[i] = t[b[i]];
msort(1, n);
printf("%d", ans);
return 0;
}

浙公网安备 33010602011771号