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
    评论
### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值