E. Binary Inversions

E. Binary Inversions

You are given a binary array$^{\dagger}$ of length $n$. You are allowed to perform one operation on it at most once. In an operation, you can choose any element and flip it: turn a $0$ into a $1$ or vice-versa.

What is the maximum number of inversions$^{\ddagger}$ the array can have after performing at most one operation?

$^\dagger$ A binary array is an array that contains only zeroes and ones.

$^\ddagger$ The number of inversions in an array is the number of pairs of indices $i,j$ such that $i<j$ and $a_i > a_j$.

Input

The input consists of multiple test cases. The first line contains an integer $t$ ($1 \leq t \leq {10}^{4}$) — the number of test cases. The description of the test cases follows.

The first line of each test case contains an integer $n$ ($1 \leq n \leq 2 \cdot {10}^{5}$) — the length of the array.

The following line contains $n$ space-separated positive integers $a_1, a_2, \ldots, a_n$ ($0 \leq a_i \leq 1$) — the elements of the array.

It is guaranteed that the sum of $n$ over all test cases does not exceed $2 \cdot {10}^{5}$.

Output

For each test case, output a single integer — the maximum number of inversions the array can have after performing at most one operation.

Example

input

5
4
1 0 1 0
6
0 1 0 0 1 0
2
0 0
8
1 0 1 1 0 0 0 1
3
1 1 1

output

3
7
1
13
2

Note

For the first test case, the inversions are initially formed by the pairs of indices ($1, 2$), ($1, 4$), ($3, 4$), being a total of $3$, which already is the maximum possible.

For the second test case, the inversions are initially formed by the pairs of indices ($2, 3$), ($2, 4$), ($2, 6$), ($5, 6$), being a total of four. But, by flipping the first element, the array becomes ${1, 1, 0, 0, 1, 0}$, which has the inversions formed by the pairs of indices ($1, 3$), ($1, 4$), ($1, 6$), ($2, 3$), ($2, 4$), ($2, 6$), ($5, 6$) which total to $7$ inversions which is the maximum possible.

 

解题思路

  注意到这题本质就是问在最多置换一位的情况下,序列的最大逆序对个数是多少。

前缀后缀和

  先给出最容易想到的做法。

  这种做法是对暴力枚举的优化,把求改变后的序列的逆序对个数优化到$O(1)$。维护序列前缀中 1 的个数和维护序列后缀中 0 的个数,同时假设原序列的逆序对个数为$\text{sum}$,然后枚举每一位,

  1. 如果$a_i = 0$,那么要把$a_i$置换成$1$,此时改变后的序列的逆序对个数应该是,原来总的逆序对个数$\text{sum}$减去第$i$个位置的逆序对个数(即第$i$个位置之前$1$的个数,也就是${\text{pre}}_{i-1}$),由于$a_i$变成了$1$因此第$i$个位置之后所有是$0$的位置的逆序对个数都增加了$1$个,所以还要加上${\text{suf}}_{i+1}$(${\text{suf}}_{i+1}$表示第$i$个位置之后$0$的个数)。所以变化后的序列的逆序对个数就是$\text{sum} - {\text{pre}}_{i-1} + {\text{suf}}_{i+1}$。
  2. 如果$a_i = 1$,那么要把$a_i$置换成$0$,此时改变后的序列的逆序对个数应该是,原来总的逆序对个数$\text{sum}$加上第$i$个位置的逆序对个数,由于$a_i$变成了$0$因此第$i$个位置之后所有是$0$的位置的逆序对个数都减少了$1$个,所以还要减去${\text{suf}}_{i+1}$。所以变化后的序列的逆序对个数就是$\text{sum} + {\text{pre}}_{i-1} - {\text{suf}}_{i+1}$。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10;
 7 
 8 int a[N], pre[N], suf[N];
 9 
10 void solve() {
11     int n;
12     scanf("%d", &n);
13     LL sum = 0;    // 原序列总的逆序对个数
14     pre[0] = 0, suf[n + 1] = 0;
15     for (int i = 1; i <= n; i++) {
16         scanf("%d", a + i);
17         pre[i] = pre[i - 1] + a[i];    // 前缀和,pre[i]表示包括i前面1的个数
18         if (a[i] == 0) sum += pre[i];
19     }
20     for (int i = n; i; i--) {
21         suf[i] = suf[i + 1] + !a[i];    // 后缀和,suf[i]表示包括i后面0的个数
22     }
23     LL ret = sum;
24     for (int i = 1; i <= n; i++) {
25         // 如果a[i] = 1,则置换变成0,sum先加上i这个位置的逆序对个数,即加上pre[i-1],
26         // 由于a[i]变成0,i+1后面所有是0的位置逆序对个数都要减少一个,一共减少了suf[i+1]个
27         if (a[i]) ret = max(ret, sum + pre[i - 1] - suf[i + 1]);
28         // 如果a[i] = 0,则置换变成1,sum先减去i这个位置的逆序对个数,即减去pre[i-1],
29         // 由于a[i]变成1,i+1后面所有是0的位置逆序对个数都要增加一个,一共增加了suf[i+1]个
30         else ret = max(ret, sum - pre[i - 1] + suf[i + 1]);
31     }
32     printf("%lld\n", ret);
33 }
34 
35 int main() {
36     int t;
37     scanf("%d", &t);
38     while (t--) {
39         solve();
40     }
41     
42     return 0;
43 }

 

贪心

  比赛的时候虽然是猜出来的做法,但我感觉并不直观,官方题解也没给出这种做法的正确性证明,因此来补充一下较为严格的证明。  

  先说结论,答案就是对下面三种情况取最大值:

  1. 统计$0$次操作时(即原序列)逆序对的个数。
  2. 从左往右找到第一次出现的$0$,置换成$1$,再求逆序对的个数。
  3. 从右往左找到第一次出现的$1$,置换成$0$,再求逆序对的个数。

  其实是先猜做法再证明正确性的。容易想到最暴力的做法是枚举每一位并置换,然后对新的序列求逆序对。这种做法的时间复杂度是$O(n^2)$,会超时。

  无非就是把每一个$0$变成$1$求一次逆序对,或者是把每一个$1$变成$0$求一次,因此如果要优化的话容易想到是否存在某个位置的$0$,在置换这个位置的$0$后得到的逆序对个数比置换其他位置的$0$要多(同理$1$)。

  假设序列中$0$的个数为$x$,并假设$s[k]$ $(1 ≤ k ≤ x)$ ,表示序列中第$k$个$0$左边$1$的个数(理解为这个位置的逆序对个数),容易知道$s[k]$是个递增序列。

  当不执行操作时,整个序列的逆序对个数就是$\sum\limits_{k=1}^{x}{s[k]}$。现在置换第$i$个位置的$0$ $(1 \leq i \leq x)$,那么逆序对个数就变成$\sum\limits_{k=1}^{x}{s[k]} + (x-i) - s[i]$。注意到$\sum\limits_{k=1}^{x}{s[k]} + x$是定值,而$-(i + s[i])$是变化的量,由于随着$i$的递增,$i + s[i]$会递增,因此$-(i + s[i])$会递减,因此$\sum\limits_{k=1}^{x}{s[k]} + (x-i) - s[i]$会递减,因此要使得逆序对最大,那么$i$应该取最小值,即应该置换第一个$0$的位置。

  同理证明$1$置换成$0$的情况,我们只需注意到求逆序对还可以根据$1$的位置往右统计$0$的个数(上面的做法是根据$0$的位置统计左边$1$的个数,这两种统计方法的等价的)。那么我们reverse整个序列,此时就变成了根据$1$的位置统计左边$0$的个数,根据上面的证明可以知道应该置换左边第一个$1$的位置能够得到最大逆序对,注意由于这是对于reverse后的序列的说法,因此对于原序列应该是置换最后一个$1$的位置。

  最后对这三种情况取最大值就可以了。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10;
 7 
 8 int n;
 9 int a[N];
10 
11 LL get() {
12     LL ret = 0;
13     for (int i = 1, s = 0; i <= n; i++) {
14         if (a[i] == 0) ret += s;
15         s += a[i];
16     }
17     return ret;
18 }
19 
20 void solve() {
21     scanf("%d", &n);
22     for (int i = 1; i <= n; i++) {
23         scanf("%d", a + i);
24     }
25     LL ret = get();
26     for (int i = 1; i <= n; i++) {
27         if (a[i] == 0) {
28             a[i] = 1;
29             ret = max(ret, get());
30             a[i] = 0;
31             break;
32         }
33     }
34     for (int i = n; i; i--) {
35         if (a[i] == 1) {
36             a[i] = 0;
37             ret = max(ret, get());
38             a[i] = 1;
39             break;
40         }
41     }
42     printf("%lld\n", ret);
43 }
44 
45 int main() {
46     int t;
47     scanf("%d", &t);
48     while (t--) {
49         solve();
50     }
51     
52     return 0;
53 }

 

参考资料

  Codeforces Round #835 (Div. 4) Editorial:https://codeforces.com/blog/entry/109348

posted @ 2022-11-23 09:56  onlyblues  阅读(88)  评论(0编辑  收藏  举报
Web Analytics