01串1: count 1
source: arc137_b
给定一个长度为 \(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:
首先,这道题修改一下问题,通过一次区间修改,可以得到最多的1的个数是多少。
我们可以暴力的去想,枚举[l,r],进行翻转,原始序列中1的个数sum, 要减去区间内1的个数,加上区间内0的个数。我们可以用前缀和o(1)求出区间内0的个数和1的个数。然后减去1的个数,加上0的个数。求一个max,$n^2$60分。
减去区间内1的个数,加上区间内0的个数,就是+1,-1操作。
于是有:假设你取了一段区间 [l,r],无论你下次取 [l−1,r]、[l+1,r]、[l,r−1] 还是 [l,r+1],都会对 ans 产生 1 的影响。这样的结论。
那么ans 的取值范围肯定是一段连续的正整数。
我们将原始序列中是1的地方,设b[]数组为-1,原数组是0的地方b[]数组是1。
原始序列中1的个数sum + b数组最大连续和,即为一次区间修改后的最多1的个数值。
同理,sum + b数组最小连续和,即为一次区间修改后的最少1的个数值。
最小~最大,就是得分的种类。
#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;
}
01串2:「KDOI-07」n1gr tS0i
source: P10877 「KDOI-07」n1gr tS0i
题目背景
众所周知,小 T 不喜欢 01 串问题,于是小 R 出了另一个有关 01 串的题目:
题目描述
有一个长度为 \(n\) 的 \(\tt 01\) 串 \(S\),你要对 \(S\) 进行 恰好 \(n\) 次操作。每次操作选择 \(1 \leq l \color{red}< \color{normal} r \leq n\),然后你按位翻转 \(S_{l\dots r}\)。这里的按位翻转指,\(S_{l\dots r}\) 内所有 \(\tt 0\) 同时变为 \(\tt 1\),且所有 \(\tt 1\) 同时变为 \(\tt 0\)。
求 \(n\) 次操作后,所有可能不同的 \(S\) 的个数。因为答案可能很大,所以请对 \(998244353\) 取模。
输入格式
本题有多组数据。
第一行一个整数 \(T\) 描述数据组数。对于每组数据:
- 第一行一个整数 \(n\)。
- 接下来一行,一个长度为 \(n\) 的 \(\tt 01\) 串 \(S\)。
输出格式
对于每组数据,一行一个整数,表示答案,对 \(998244353\) 取模后的结果。
样例 #1
样例输入 #1
2
2
01
30
101001001010100110101101011110
样例输出 #1
1
75497471
提示
样例解释
- 对于 \(n = 2\),\(S = \tt 01\),我们会发现每次操作只能选择 \(l = 1, r = 2\) 即反转整串,因此 \(2\) 次操作后只能得到 \(\tt 01\),故答案为 \(1\);
- 对于第二组数据,暂时不能给你一个明确的答复。
数据规模与约定
本题采用捆绑测试。
\(\text{Subtask}\) | \(n\le\) | 分数 |
---|---|---|
\(1\) | \(4\) | \(30\) |
\(2\) | \(10^5\) | \(70\) |
对于所有数据,保证 \(2 \leq n \leq 10^5\),\(\sum n \leq 10^6\),\(1 \leq T \leq 10^4\)。
Analysis:
这里有一个很重要的条件L<R,如果L等于R,是不是每一个01序列都可以有\(2^n\)种。现在尝试找规律:
我们可以用两次操作将一个n>=4的序列任意一位通过两次变化变为0或者1,比如“10110”的第4位,我想让它变成0,可以将它放在一个三位区间的最右侧,就是[011],这样,我们将这个区间翻转成100,那么第4位的1已经变成0了,达到我们的预期了。但是前两个数字也被我们修改了,于是我们把[10]再次翻转,就变成了原来的[01]。如果我想让第4位不变,只需要翻转包含它的两位数字两次,相当于没有修改。
如果要修改两位,其余都不变,比如10110中的[11],也是可以两次,[1011]变成[0100]再变成[1000]。
这个序列一定是要更改一些连续区间段,[]...[]....[],每一个连续区间段至少可以用2次更改,最多分成n/2个区间段,这样有2*n/2去更改成对立状态。
总状态是2^n.
如果n==3,则中间的哪位没有办法用上述方法修改。因为修改的数字要放在区间的边上。
n<=3的单独算一下即可。