Hanks_o的博客

有权限号的bzoj蒟蒻用户

bzoj2958&3269: 序列染色(Dp)

题目传送门
玄学真的不会。

解法:
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;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Hanks_o/article/details/79959285
个人分类: BZOJ Dp
上一篇bzoj5196: [Usaco2018 Feb]Taming the Herd(Dp)
下一篇bzoj1858: [Scoi2010]序列操作(线段树)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭