T1 [JZOJ6314] Balancing Inversions
题目描述
$Bessie$ 和 $Elsie$ 在一个长为 $2N$ 的布尔数组 $A$ 上玩游戏。
$Bessie$ 的分数为 $A$ 的前一半的逆序对数量,$Elsie$ 的分数为 $A$ 的后一半的逆序对数量。
逆序对指的是满足 $A[i]=1$ 以及 $A[j]=0$ 的一对元素,其中 $i<j$。例如,一段 $0$ 之后接着一段 $1$ 的数组没有逆序对,一段 $X$ 个 $1$ 之后接着一段 $Y$ 个 $0$ 的数组有 $XY$ 个逆序对。
$Farmer \; John$ 偶然看见了这一棋盘,他好奇于可以使得游戏看起来成为平局所需要交换相邻元素的最小次数。请帮助 $Farmer \; John$ 求出这个问题的答案。
数据范围
前 $4$ 组数据 $N$ 分别为 $5,20,100,1000$
对于 $100\%$ 的数据,$1 \leq N \leq 10^5$
分析
考场上一直在想这题,绕了好多弯子,到最后十分钟差不多想清楚,但是没码完
显然,当不交换中间两元素时,最小交换次数为前后两半的逆序对数差的绝对值
然后可以先枚举将左边 $k$ 个 $1$ 移到右边时的所有情况,同理再枚举移 $0$ 的情况
以移 $1$ 为例,我们可以用两个指针分别指向前半段的尾部和后半段的首部,然后依次向前查找 $1$ 和向后查找 $0$,这样总时间复杂度就是 $O(n)$ 的了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <queue> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 100005 int n, cnt1, cnt2, pos1, pos2; ll an, bn, ans, len1, len2; int a[N], b[N]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", a + i); if (a[i]) cnt1++; else an += cnt1; } for (int i = 1; i <= n; i++) { scanf("%d", b + i); if (b[i]) cnt2++; else bn += cnt2; } ans = abs(an - bn); pos1 = n; pos2 = 1; len1 = len2 = 0; for (int i = 1; i <= cnt1 && i <= n - cnt2; i++) { while (!a[pos1]) pos1--; len1 += n - pos1; pos1--; while (b[pos2]) pos2++; len2 += pos2 - 1; pos2++; ll sum1 = an - len1 + i * (cnt1 - i); ll sum2 = bn - len2 + i * (n - cnt2 - i); ll now = len1 + len2 + i + abs(sum1 - sum2); ans = min(ans, now); } pos1 = n; pos2 = 1; len1 = len2 = 0; for (int i = 1; i <= n - cnt1 && i <= cnt2; i++) { while (a[pos1]) pos1--; len1 += n - pos1; pos1--; while (!b[pos2]) pos2++; len2 += pos2 - 1; pos2++; ll sum1 = an + len1 - i * cnt1; ll sum2 = bn + len2 - i * (n - cnt2); ll now = len1 + len2 + i + abs(sum1 - sum2); ans = min(ans, now); } printf("%lld", ans); return 0; }