您的位置:首页 > 其它

BZOJ2958: 序列染色

2016-12-28 17:29 232 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: