raywu的博客

博客

# 8052. Dot Product 题解

2024-11-15 15:31:56 By raywu

QOJ 8052 题解。

联考 T1 考到了这个题,全机房都过了,就我不会呜呜呜。

Task

给长度为 $n$ 的排列 $a$。一次操作可以交换 $a$ 相邻的两个数,问最少多少次操作后 $a$ 满足 $\forall 1 \le i < j \le n, a_i i \le a_j j$。

$n \le 5 \times 10^5$。

Solution

记 $pos_{a_i} = i$。

考虑如果最终的排列 $a$ 如果满足 $a_i = i$,则一定合法,操作次数为逆序对数。

然而不止有这一种最终排列方式。如果交换两个相邻的数,变成:$1, 2, \cdots, x, x + 2, x + 1, x + 3, \cdots, n$,同样是合法的。

可以发现,最终排列一定可以 $a_i = i$ 的排列通过交换若干个不相交的相邻数得到。

考虑一个 $pos_i > pos_{i + 1}$,说明 $i$ 原本在 $i + 1$ 后面,最后只需交换成 $i + 1, i$ 即可,无需再花费一次操作交换为 $i, i + 1$。

用逆序对数减去 $\sum_{i = 1}^{n - 1}[pos_i > pos_{i + 1}]$ 即可。

复杂度 $O(n \log n)$。

Code

#include <bits/stdc++.h>
#define _for(i, a, b)  for (int i = (a); i <= (b); i ++ )
#define ll long long
using namespace std;
const int N = 1e6 + 5;
int n, pos[N]; ll ans;
struct BIT {
    int tr[N];
    inline int lowbit(int x) { return (x & ( - x)); }
    inline void update(int x, int k) { for (int i = x; i <= n; i += lowbit(i))  tr[i] += k; }
    inline int query(int x) {
        int res = 0;
        for (int i = x; i; i -= lowbit(i))  res += tr[i];
        return res;
    }
    inline int query(int l, int r) { return query(r) - query(l - 1); }
} T;
inline void solve() {
    cin >> n, ans = 0; int x;
    _for (i, 1, n)  cin >> x, pos[x] = i, ans += T.query(x + 1, n), T.update(x, 1);
    _for (i, 1, n - 1)  if (pos[i] > pos[i + 1])  ans -- , i ++ ;
    cout << ans << "\n";
    _for (i, 1, n)  T.update(i, - 1);
}
int main() {
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T -- )  solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。