AT3950 [AGC022E] Median Replace

题目传送门

Description

有一个长度为 (n)(01) 串,里面有一些还没有确定,我们标记为 ? 。可以进行若干次操作,每次操作可以把三个相邻的数替换成它们的中位数。问有多少种把 ? 填为 0/1的方法使得该 (01) 串最后能变为 (1)

Solution

我们先考虑如何判断一个 (01) 串是否合法。

可以想到的是,我们的 (000) 一定要先删掉。然后考虑 (01,10) ,因为如果出现这两个,那么无论再有 (0,1) 都由新加入的值决定,所以可以直接删去。最后考虑 (111)

我们如果用栈来处理。就是:

  1. 如果当前值为 (1)

如果栈顶为 (0),则删去栈顶。如果栈顶为 (1) 且栈顶下一个不是 (1),就加入,否则不管

  1. 如果当前值为 (0)

如果栈顶有两个 (0) 就直接删掉一个栈顶,否则就直接加入。至于不管栈顶为 (1) 的情况是因为我们需要先处理 (000) 而不是 (10,01)


于是我们就可以考虑用 dp 解决这个问题。我们设 (f_{i,j,k}) 表示前面 (i) 个数,栈有 (j)(1)(k)(0)。可以看出的是我们栈的形态从栈底到栈顶一定是一段 (1) 加上一段 (0)。因为如果有 (01) 的存在就一定会直接删掉。而且,(0,1) 的个数都不会超过 (2),因为超过就会被删掉或者没有意义了。转移按上面的方法转移即可。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 300005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

char s[MAXN];
int n,f[MAXN][4][4];

#define mod 1000000007
int add (int &a,int b){return a = a + b >= mod ? a + b - mod : a + b;}

signed main(){
	scanf ("%s",s + 1),n = strlen (s + 1),f[0][0][0] = 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][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]);
				}
			}
	}
	int ans = 0;
	for (Int i = 0;i < 3;++ i)
		for (Int j = 0;j <= i;++ j)
			add (ans,f[n][i][j]);
	write (ans),putchar ('
');
 	return 0;
}
原文地址:https://www.cnblogs.com/Dark-Romance/p/14279586.html