NOIP 2013 提高组 火柴排队 题解

原题链接

前置知识:置换与排列

在做本题前,最好先做过 洛谷P1908 逆序对

Solution

假设当前处在最优解,那么交换某列火柴中的任意相邻位置,两列火柴间的距离均变大。

不妨设交换 \(a_i\)\(a_{i+1}\),则有

\[\Delta =\left[ (a_{i+1}-b_i)^2 + (a_i-b_{i+1})^2 \right] - \left[ (a_i-b_i)^2 + (a_{i+1}-b_{i+1})^2 \right] = 2(a_i-a_{i+1})(b_i-b_{i+1}) > 0 \]

火柴高度值域过大,考虑先对其离散化。由于同一列火柴的高度互不相同,离散化后会得到两个 \(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;
}
posted @ 2026-04-03 23:18  SHUddol  阅读(5)  评论(0)    收藏  举报