AGC022E Median Replace

题目大意

你有一个长度为$n$的串$\texttt{S}$,其中有一些位置上的字符是?,其他的字符则是$0/1$之间的一种

每次可以进行一步操作:选择$3$个连续的字符,并把它们用它们的中位数替换

求有多少种把?替换成$0/1$的方案使得在进行$\frac{n-1}{2}$次操作后剩下的字符为$1$?

分析

我们先考虑如果给定一个符合条件的串,要怎么判定这个串是否合法。

我们维护一个栈,这个栈从栈底到栈顶由一段连续的$1$和一段连续的$0$组成

对于新的一个字符$\texttt{c}$,我们分$0/1$情况考虑

  • $c=0$,我们发现连续的$3$个$0$可以被抵消成为$1$个$0$,所以如果原来栈顶有$2$个连续的$0$,那么就把这$3$个$0$抵消掉$2$个变成$1$个$0$,否则直接把这个$0$插入栈顶

  • $c=1$,如果栈顶是$0$,则可以将这个$0$与$1$抵消(因为再找一个数,$3$个数取中位数的话,结果只与新找的数有关),否则把这个$1$插入栈。(如果栈中已经有了两个$1$,则怎么合并剩下的都是$1$,所以如果栈中已经有了两个$1$就可以忽略新的这个$1$了)

然后我们发现栈中$1$的个数只有$0\sim2$这$3$种情况,$0$的个数也只有$0\sim2$这$3$种情况,所以栈的种类数只有$3 \times 3 = 9$种

现在我们考虑怎么$\texttt{dp}$:我们可以把当前栈的状态当做$\texttt{dp}$的状态,设$f[i][j][k]$表示当前处理第$i$位,栈中有$j$个$1$和$k$个$0$,则我们就可以按照上述的方式转移,对于?只要当做$0/1$分别转移一次就可以了。(具体转移可参见代码)

时间复杂度$\mathcal{O(n)}$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>

#define R register
#define ll long long
#define sum(a, b, mod) (((a) + (b)) % mod)

const int mod = 1e9 + 7;
const int MaxN = 3e5 + 10;

char s[MaxN];
ll n, f[MaxN][3][3];
void add(ll &a, ll b) {a += b, ((a > mod) ? (a -= mod) : 0); }

int main()
{
f[0][0][0] = 1;
scanf("%s", s + 1), n = strlen(s + 1);
for(int i = 0; i < n; i++)
{
for(int j = 0; j < 3; j++)
{
for(int k = 0; k < 3; k++)
{
if(s[i + 1] != '0')
{
if(k)
add(f[i + 1][j][k - 1], f[i][j][k]);
else add(f[i + 1][std::min(j + 1, 2)][k], f[i][j][k]);
}
if(s[i + 1] != '1')
{
if(k == 2)
add(f[i + 1][j][1], f[i][j][k]);
else add(f[i + 1][j][k + 1], f[i][j][k]);
}
}
}
}
ll ans = 0;
for(int i = 0; i < 3; i++)
for(int j = 0; j <= i; j++)
add(ans, f[n][i][j]);
printf("%lld\n", ans);
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×