給定一個長度為 \(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;
}