bzoj2958&3269: 序列染色(Dp)

Hanks_o發表於2018-04-16

題目傳送門
玄學真的不會。

解法:
f[i][j][k]表示第i位,k=0填B,k=1填W。
j=0表示一段長為K連續的B都沒有。
j=1表示有一段長為K連續的B。
j=2表示有一段長為K連續的W。
因為要先有B是連續的才要W連續。
那麼j=2肯定從j=1轉移

保證連續的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<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
int b[1110000],w[1110000],f[1110000][3][2];char ss[1110000];
const int mod=1000000007;
int main() {
    int n,k;scanf("%d%d%s",&n,&k,ss+1);
    ss[++n]='X';memset(b,0,sizeof(b));memset(w,0,sizeof(b));
    for(int i=1;i<=n;i++) {
        b[i]=b[i-1];if(ss[i]=='B')b[i]++;
        w[i]=w[i-1];if(ss[i]=='W')w[i]++;
    }
    memset(f,0,sizeof(f));f[0][0][1]=1;
    for(int i=1;i<=n;i++) {
        if(ss[i]!='W')for(int j=0;j<3;j++)f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%mod;
        if(ss[i]!='B')for(int j=0;j<3;j++)f[i][j][1]=(f[i-1][j][0]+f[i-1][j][1])%mod;
        if(i<k)continue;
        if(ss[i]!='W'&&w[i]==w[i-k]) {
            f[i][1][0]=(f[i][1][0]+f[i-k][0][1])%mod;
            f[i][0][0]=(f[i][0][0]-f[i-k][0][1])%mod;
        }if(ss[i]!='B'&&b[i]==b[i-k]) {
            f[i][2][1]=(f[i][2][1]+f[i-k][1][0])%mod;
            f[i][1][1]=(f[i][1][1]-f[i-k][1][0])%mod;
        }
    }printf("%d\n",((f[n][2][0])%mod+mod)%mod);
    return 0;
}

相關文章