BZOJ2958: 序列染色

Description

  给出一个长度为N由B、W、X三种字符组成的字符串S,你需要把每一个X染成B或W中的一个。
  对于给出的K,问有多少种染色方式使得存在整数a,b,c,d使得:
  1<=a<=b<c<=d<=N
  Sa,Sa+1,...,Sb均为B
  Sc,Sc+1,...,Sd均为W
  其中b=a+K-1,d=c+K-1
  由于方法可能很多,因此只需要输出最后的答案对109+7取模的结果。

Input

  第一行两个正整数N,K
  第二行一个长度为N的字符串S

Output


  一行一个整数表示答案%(109+7)。

Sample Input


5 2
XXXXX

Sample Output


4
数据约定
  对于20%的数据,N<=20
  对于50%的数据,N<=2000
  对于100%的数据,1<=N<=10^6,1<=K<=10^6

HINT

Source

乱dp
最先我考虑的是dp[i][3]表示当前第i位,然后状态为0/1/2的方案数
发现会算重
变成dp[i][3][2]表示当前这一位是'B'还是'W' 然后只转移第一次能转移的 一定是从'B'之后放K个'W'
还有一个问题 会算重 如中间k=3染成'BBB'仍然会计算成0的状态 那么我们提前减掉就行了
#include <bits/stdc++.h>

using namespace std;

const int maxn = 1000010;
const int mod = 1e9 + 7;

char s[maxn];

bool b[maxn], w[maxn];

int n, m;

int dp[maxn][3][2];

int vis[2];

inline void upd(int &x, int y) { x += y; if( x >= mod ) x -= mod; if( x < 0 ) x += mod; }

int main()
{
	scanf( "%d%d", &n, &m );
	scanf( "%s", s + 1 );
	for( int i = 1 ; i <= n ; i++ )
	{
		if( s[ i ] == 'W' ) vis[ 0 ] ++;
		if( s[ i ] == 'B' ) vis[ 1 ] ++;
		if( i >= m )
		{
			if( !vis[ 0 ] ) b[ i - m + 1 ] = 1;
			if( !vis[ 1 ] ) w[ i - m + 1 ] = 1;
			if( s[ i - m + 1 ] == 'W' ) vis[ 0 ] --;
			if( s[ i - m + 1 ] == 'B' ) vis[ 1 ] --;
		}
	}
	dp[ 0 ][ 0 ][ 1 ] = 1;
	for( int i = 0 ; i < n ; i++ )
	{
		if( b[ i + 1 ] ) upd( dp[ i + m ][ 1 ][ 0 ], dp[ i ][ 0 ][ 1 ] ),
						 upd( dp[ i + m ][ 0 ][ 0 ], -dp[ i ][ 0 ][ 1 ] );
		if( w[ i + 1 ] ) upd( dp[ i + m ][ 2 ][ 1 ], dp[ i ][ 1 ][ 0 ] ),
						 upd( dp[ i + m ][ 1 ][ 1 ], - dp[ i ][ 1 ][ 0 ] );
		for( int j = 0 ; j <= 2 ; j++ )
			for( int k = 0 ; k < 2 ; k++ )
			{
				if( s[ i + 1 ] != 'B' )
					upd( dp[ i + 1 ][ j ][ 1 ], dp[ i ][ j ][ k ] );
				if( s[ i + 1 ] != 'W' )
					upd( dp[ i + 1 ][ j ][ 0 ], dp[ i ][ j ][ k ] );
			}
	}
	upd( dp[ n ][ 2 ][ 0 ], dp[ n ][ 2 ][ 1 ] );
	cout << dp[ n ][ 2 ][ 0 ] << endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值