BZOJ 2958&3269: 序列染色

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

HINT

对于100%的数据,1<=N<=106,1<=K<=106

用f[i][j][k]表示到了第i位,状态是j(即0是没有连续段长为k的,1是B有W没有,2是BW都有),k是这一位选了什么(0是B,1是W)

保证连续的B后面一定有一个W,连续的W后有一个B,答案就是f[n+1][2][0]

假设这一位选B显然有 

f[i][j][0]=f[i-1][j][1]+f[i-1][j][0]

if(i-k+1到i没有W)f[i][1][0]=f[i][1][0]+f[i-k][0][1];

然而这样会有重复,由于f[i][0][0]是乱转移,中间有可能已经有B了,所以:

if(i-k+1到i没有W)f[i][0][0]=f[i][0][0]-f[i-k][0][1];

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 1000100
#define mod 1000000007
using namespace std;
int w[maxn],b[maxn],dp[maxn][3][2],n,k;
char s[maxn];
int inline get(int x){
//	if(x>=mod)return x-mod;
//	else if(x<=-mod)return x+mod;
	return x%mod;
}
int main(){
	scanf("%d%d%s",&n,&k,s+1);
	s[++n]='X';
	for(int i=1;i<=n;++i){
		b[i]=b[i-1]+(s[i]=='B');
		w[i]=w[i-1]+(s[i]=='W');
	}
	dp[0][0][1]=1;
	for(int i=1;i<=n;++i){
		if(s[i]=='B'||s[i]=='X')for(int j=0;j<3;++j)dp[i][j][0]=get(dp[i-1][j][1]+dp[i-1][j][0]);
		if(s[i]=='W'||s[i]=='X')for(int j=0;j<3;++j)dp[i][j][1]=get(dp[i-1][j][1]+dp[i-1][j][0]);
		if(i<k)continue;
		if(s[i]=='B'||s[i]=='X')if(w[i]==w[i-k]){
			dp[i][1][0]=get(dp[i][1][0]+dp[i-k][0][1]);
			dp[i][0][0]=get(dp[i][0][0]-dp[i-k][0][1]);
		} if(s[i]=='W'||s[i]=='X')if(b[i]==b[i-k]){
			dp[i][2][1]=get(dp[i][2][1]+dp[i-k][1][0]);
			dp[i][1][1]=get(dp[i][1][1]-dp[i-k][1][0]);
		}
	}
	printf("%d\n",(dp[n][2][0]%mod+mod)%mod);
}



link to my blog

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值