【BZOJ2958】序列染色(动态规划)

点此看题面

大致题意: 给定一个由"X","B","W"三种字符组成的字符串,让你把所有"X"替换为"B"或"W",使得存在一段长度为\(m\)、全是"B"的子串,且其后还存在一段长度为\(m\)、全是"W"的子串。求方案数。

动态规划

发现现在做题总是把题目想复杂,其实这道题还是比较简单的。。。

我们设\(f_{i,0/1},g_{i,0/1},h_{i,0/1}\)分别表示在前\(i\)个字符中不存在"B"的子串存在"B"的子串但其后不存在"W"的子串满足了题目的要求,且最后一个选择的字符是"B"/"W"的方案数。

首先,如果这一位上不是"B"(即是"X"或"W"),那么我们可以直接从上一位转移得到\(f_{i,0},g_{i,0},h_{i,0}\),同理在这一位上不是"W"时可以直接转移得到\(f_{i,1},g_{i,1},h_{i,1}\)

但仅仅这样转移肯定是不够的,我们还需要考虑从一种状态到另一种状态的过渡转移。

即,如果之前的\(m\)位不存在"W"(即可以全部染成"B"),我们就令\(f_{i,1}\)减去\(f_{i-m,0}\)\(g_{i,1}\)加上\(f_{i-m,0}\)

注意要从第二维为\(0\)的情况转移,是为了避免一段长度大于\(m\)的子串被计算多次。

同理,如果之前的\(m\)为不存在"B",我们就令\(g_{i,0}\)减去\(g_{i-m,1}\)\(h_{i,0}\)加上\(g_{i-m,1}\)

于是这道题就做完了,最终答案就是\(h_{n,0}+h_{n,1}\)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
using namespace std;
int n,m,f[N+5][2],g[N+5][2],h[N+5][2],s[N+5][2];char st[N+5];
int main()
{
	RI i;for(scanf("%d%d%s",&n,&m,st+1),i=1;i<=n;++i)
		s[i][0]=s[i-1][0],s[i][1]=s[i-1][1],st[i]^'X'&&++s[i][st[i]=='B'];//预处理前缀和以判断一段区间内是否存在某个数
	for(f[0][0]=i=1;i<=n;++i)//初始化时默认第0位填0
		st[i]^'B'&&(f[i][0]=(f[i-1][0]+f[i-1][1])%X,g[i][0]=(g[i-1][0]+g[i-1][1])%X,h[i][0]=(h[i-1][0]+h[i-1][1])%X),//可填0
		st[i]^'W'&&(f[i][1]=(f[i-1][0]+f[i-1][1])%X,g[i][1]=(g[i-1][0]+g[i-1][1])%X,h[i][1]=(h[i-1][0]+h[i-1][1])%X),//可填1
		i>=m&&s[i-m][0]==s[i][0]&&(Dec(f[i][1],f[i-m][0]),Inc(g[i][1],f[i-m][0])),//可以得到一段B串
		i>=m&&s[i-m][1]==s[i][1]&&(Dec(g[i][0],g[i-m][1]),Inc(h[i][0],g[i-m][1]));//可以得到一段W串
	return printf("%d\n",(h[n][0]+h[n][1])%X),0;//输出答案
}
posted @ 2020-06-16 16:22  TheLostWeak  阅读(14)  评论(0编辑  收藏