arc137_b

Jeanny發表於2024-08-22

給定一個長度為 \(n\) 的由 \(0,1\) 組成的整數序列 \(A=(A_1,A_2,\cdots,A_n)\) 。你可以做以下的操作一次且僅一次

  • 選擇 \(A\) 的一個連續的子段,對該子段進行反轉操作,也就是將 \(0\) 變成 \(1\) ,將 \(1\) 變成 \(0\) 。注意,你可以選擇一個空欄位,這就相當於你什麼都沒有做。

最後 \(A\) 中的 \(1\) 的個數,是你能獲得的分數。請問你有多少種可能的得分。

樣例 #1

樣例輸入 #1

4
0 1 1 0

樣例輸出 #1

4

樣例 #2

樣例輸入 #2

5
0 0 0 0 0

樣例輸出 #2

6

樣例 #3

樣例輸入 #3

6
0 1 0 1 0 1

樣例輸出 #3

3

提示

  • $ 1\ \leq\ N\ \leq\ 3\ \times\ 10^5 $
  • $ 0\ \leq\ A_i\ \leq\ 1 $

analysis:

首先,設 ans為操作後陣列中 1 的個數。那麼就有一個結論:ans 的取值範圍肯定是一段連續的正整數。

因為假設你取了一段區間 [l,r],無論你下次取 [l−1,r]、[l+1,r]、[l,r−1] 還是 [l,r+1],都會對 ans 產生 1 的影響。

所以我們可以想到,求 ans 取到的最大值 ansmx 和最小值 ansmn,那麼 ans的取值的數量就為 [ansmn,ansmx]的元素個數。

如何求這個值?

考慮到每次多取一個數就會對 ans 產生 1 的影響,所以可以定義陣列 s,si​ 為取 ai​ 對答案造成的影響,那麼 si​ 是由 1 或 −1−1 組成的。

那麼就可以找區間和最大的 s 區間,與區間和最小的 ss 區間。

所以可以想到字首和。

用字首和維護區間和最大值和最小值不多贅述。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, a[300005], b[300005], maxn, minn, ansmax, ansmin;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]), b[i] = b[i - 1] + (a[i] == 0 ? 1 : -1);
    for (int i = 1; i <= n; i++) {
        ansmax = max(ansmax, b[i] - minn);
        //b[i]-minn 就是以當前節點為結尾所能產生的最大值
        ansmin = min(ansmin, b[i] - maxn);
        //b[i]-maxn 就是以當前節點為結尾所能產生的最小值
        maxn = max(maxn, b[i]);
        minn = min(minn, b[i]);
    }
    printf("%d", ansmax - ansmin + 1);
    return 0;
}