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
由于方法可能很多,因此只需要输出最后的答案对10^9+7取模的结果。
Input
第一行两个正整数N,K
第二行一个长度为N的字符串S
1<=N<=106,1<=K<=106
Output
一行一个整数表示答案%(10^9+7)。
题解:
f[i][j][k]
j:0,1,2表示当前B,W都不合法,B合法,都合法
k:当前位填了B/W
如何去重(直接dp长度超过k 的可行段会加重)
- 对于一个可行段,只考虑最后k个(只从k - 1个位置和后k个不同转移过来)
- 减去一定可行却仍当成不可行在统计的方案
思考的时候去重的方法没有想到。想了30min都没有想出来,看了题解才知道。思考速度和效率都不行啊!
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define repd(i,a,b) for(int i=a;i>=b;--i)
#define rvc(i,S) for(int i=0;i<(int)S.size();++i)
#define fore(i,x) for(int i = head[x] ; i ; i = e[i].next)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define lowbit(x) (x&(-x))
using namespace std;
#define maxn 1000020
const int mod = 1e9 + 7;
int w[maxn],b[maxn],f[maxn][3][2];
int n,k;
char ch[maxn];
inline void up(int &x,int y){
x += y;
if ( x >= mod ) x -= mod;
else if ( x < 0 ) x += mod;
}
int main(){
scanf("%d %d",&n,&k);
scanf("%s",ch + 1);
rep(i,1,n){
w[i] = w[i - 1] + (ch[i] == 'W');
b[i] = b[i - 1] + (ch[i] == 'B');
}
/* if ( ch[1] == 'X' )
f[1][0][0] = f[1][0][1] = 1;
else if ( ch[1] == 'B' )
f[1][0][0] = 1;
else
f[1][0][1] = 1; */
f[0][0][0] = 1;
rep(i,1,n){
if ( ch[i] == 'B' || ch[i] == 'X' )
rep(j,0,2) up(f[i][j][0],f[i - 1][j][0] + f[i - 1][j][1]);
if ( ch[i] == 'W' || ch[i] == 'X' )
rep(j,0,2) up(f[i][j][1],f[i - 1][j][1] + f[i - 1][j][0]);
if ( i < k ) continue;
if ( (ch[i] == 'B' || ch[i] == 'X') && w[i] - w[i - k] == 0 ){
up(f[i][1][0],f[i - k][0][(i == k) ? 0 : 1]);
up(f[i][0][0],-f[i - k][0][(i == k) ? 0 : 1]);
}
if ( (ch[i] == 'W' || ch[i] == 'X') && b[i] - b[i - k] == 0 ){
up(f[i][2][1],f[i - k][1][0]);
up(f[i][1][1],-f[i - k][1][0]);
}
}
printf("%d\n",(f[n][2][0] + f[n][2][1]) % mod);
}