bzoj2958 & bzoj3269

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

 中国国家队清华集训 2012-2013 第一天

 

题解:

我的算法和网上别人的好像不太一样。。。

每次转移都是一段B或一段W,所以记dp[i][j][k]为算完i位,j=0,1,2表示没有完成B,没有完成W,BW都完成了,k表示上一个是B还是W。

普通的dp直接枚举从哪一位转移,长度大于k强制修改j。

时间复杂度O(n^2)。

看到转移是把一段dp求和,记录dp的前缀和即可。

时间复杂度O(n)。

 

 

#include <stdio.h>
#include <algorithm>
 
using namespace std;
 
//long long
const int mod = 1000000007;
const int maxn = 1200000;
int n , k;
char z[maxn];
int dp[maxn][3][3] , last[maxn][3] , sum[maxn][3][3];
void work () {
    int i , j , l;
    scanf ( "%d%d" , &n , &k );
    scanf ( "%s" , z + 1 );
    if ( k > n ) {
        printf ( "0\n" );
        return ;
    }
    j = n;
    for ( i = n ; i >= 1 ; i-- ) {
        if ( j > i ) j = i;
        while ( j >= 0 && z[j] != 'W' ) j--;
        last[i][1] = j;
    }
    j = n;
    for ( i = n ; i >= 1 ; i-- ) {
        if ( j > i ) j = i;
        while ( j >= 0 && z[j] != 'B' ) j--;
        last[i][2] = j;
    }
    //for ( i = 1 ; i <= n ; i++ ) printf ( "%d %d\n" , last[i][1] , last[i][2] );
    //if ( z[1] != 'W' ) {
    //  if ( k != 1 ) dp[1][0][1] = 1;
    //  else dp[1][1][1] = 1;
    //}
    dp[0][0][1] = dp[0][0][2] = sum[0][0][1] = sum[0][0][2] = 1;
    //if ( z[1] != 'B' ) dp[1][0][2] = 1;
    //sum[1][0][1] = dp[1][0][1]; sum[1][1][1] = dp[1][1][1];
    //sum[1][0][2] = dp[1][0][2];
    for ( i = 1 ; i <= n ; i++ ) {
        if ( last[i][2] <= i ) {
            dp[i][0][2] += (sum[i-1][0][1] - (last[i][2]!=0?sum[last[i][2]-1][0][1]:0) + mod) % mod;
            dp[i][0][2] %= mod;
        }
        l = i - 1 - k + 1;
        if ( last[i][1] <= i ) {
            if ( last[i][1] <= l ) {
                dp[i][0][1] += (sum[i-1][0][2] - sum[l][0][2] + mod) % mod;
                dp[i][0][1] %= mod;
                dp[i][1][1] += (sum[l][0][2] - (last[i][1]!=0?sum[last[i][1]-1][0][2]:0) + mod) % mod;
                dp[i][1][1] %= mod;
            }
            else {
                dp[i][0][1] += (sum[i-1][0][2] - (last[i][1]!=0?sum[last[i][1]-1][0][2]:0) + mod) % mod;
                dp[i][0][1] %= mod;
            }
        }
        //printf ( "%d\n" , l );
        /*  printf ( "%d %d %d %d\n" , dp[1][0][1] , dp[1][0][2] , 
            (sum[i-1][0][1] - sum[last[i][2]-1][0][1]),
            (sum[i-1][0][2] - sum[last[i][1]-1][0][2]) );*/
        if ( last[i][2] <= i ) {
            if ( last[i][2] <= l ) {
                dp[i][1][2] += (sum[i-1][1][1] - sum[l][1][1] + mod) % mod;
                dp[i][1][2] %= mod;
                dp[i][2][2] += (sum[l][1][1] - (last[i][2]!=0?sum[last[i][2]-1][1][1]:0) + mod) % mod;
                dp[i][2][2] %= mod;
            }
            else {
                dp[i][1][2] += (sum[i-1][1][1] - (last[i][2]!=0?sum[last[i][2]-1][1][1]:0) + mod) % mod;
                dp[i][1][2] %= mod;
            }
        }
        if ( last[i][1] <= i ) {
            dp[i][1][1] += (sum[i-1][1][2] - (last[i][1]!=0?sum[last[i][1]-1][1][2]:0) + mod) % mod;
            dp[i][1][1] %= mod;
        }
        if ( last[i][2] <= i ) {
            dp[i][2][2] += (sum[i-1][2][1] - (last[i][2]!=0?sum[last[i][2]-1][2][1]:0) + mod) % mod;
            dp[i][2][2] %= mod;
        }
        if ( last[i][1] <= i ) {
            dp[i][2][1] += (sum[i-1][2][2] - (last[i][1]!=0?sum[last[i][1]-1][2][2]:0) + mod) % mod;
            dp[i][2][1] %= mod;
        }
        for ( j = 1 ; j <= 6 ; j++ ) sum[i][(j-1)/2][(j-1)%2+1] = (sum[i-1][(j-1)/2][(j-1)%2+1]+dp[i][(j-1)/2][(j-1)%2+1]) % mod;
    }
    //for ( i = 0 ; i <= n ; i++ ) printf ( "%d %d %d %d %d %d\n" , dp[i][0][1] , dp[i][0][2] , dp[i][1][1] , dp[i][1][2] , dp[i][2][1] , dp[i][2][2] );
    //printf("\n");
    //for ( i = 0 ; i <= n ; i++ ) printf ( "%d %d %d %d %d %d\n" , sum[i][0][1] , sum[i][0][2] , sum[i][1][1] , sum[i][1][2] , sum[i][2][1] ,sum[i][2][2] );
    printf ( "%d\n" , ((dp[n][2][1] + dp[n][2][2]) % mod + mod ) % mod);
}
int main () {
    //FILE *fpr = freopen ( "bzoj3269.in" , "r" , stdin );
    //FILE *fpw = freopen ( "bzoj3269.out" , "w" , stdout );
    work ();
    return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值