BZOJ 3747(递推dp)

题意:构建一个由G、R、P三个字母构成的字符串,要求出现G则必须要大于等于连续M个,出现R则必须要小于等于连续K个


脑子太笨了,不会转化,一开始无从下手,对至少M个无法处理,想着可以从之前i-m之前都转移过来。还是看了题解。


将原题转化为(至多n个G,至多k个R) - (至多m-1个G,至多k个R)。真他妈巧妙啊= =。

dp[i][0] 第i个为G,至多有u个G 和v个R的个数
dp[i][1] 第i个为R,至多有u个G 和v个R的个数
dp[i][2] 第i个为P,至多有u个G 和v个R的个数

对于i<=u的时候,怎么放都行
dp[i][0] = dp[i-1][0] + dp[i-1][1] + dp[i-1][2]
i == u+1 的时候 多了一种全是G的情况
dp[i][0] = dp[i-1][0] + dp[i-1][1] + dp[i-1][2] - 1
i > u+1的时候 我们需要计算之前多的情况,即已经连续u个G了。那么就是找到一个状态是满足i-1到i-u已经连续u个G的,就是dp[i-u-1][1] 和 dp[i-u-1][2]


#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <map>
#include <vector>
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
const int maxn = 1e6 + 5;
typedef long long LL;
//dp[i][0] 第i个为G,至多有u个G 和v个R的个数
//dp[i][1] 第i个为R,至多有u个G 和v个R的个数
//dp[i][2] 第i个为P,至多有u个G 和v个R的个数
LL dp[maxn][3];
int n,m,k;
LL solve(int u, int v){
    for(int i=1; i<=n; i++){
        LL sum = dp[i-1][0] + dp[i-1][1] + dp[i-1][2];
        if(i == 1) sum = 1;
        dp[i][2] = sum;
        if(i <= u){
            dp[i][0] = sum % mod;
            dp[i][0] %= mod;
        }
        else if(i == u+1){
            dp[i][0] = (sum - 1) % mod;;
            dp[i][0] %= mod;
        }
        else{
            dp[i][0] = (sum - dp[i-u-1][1] - dp[i-u-1][2]) % mod;
            dp[i][0] %= mod;
        }
        if(i <= v){
            dp[i][1] = sum % mod;
            dp[i][1] %= mod;
        }
        else if(i == v+1){
            dp[i][1] = (sum - 1) % mod;;
            dp[i][1] %= mod;
        }
        else{
            dp[i][1] = (sum - dp[i-v-1][0] - dp[i-v-1][2]) % mod;
            dp[i][1] %= mod;
        }
    }
    return (dp[n][0] + dp[n][1] + dp[n][2]) % mod;
}
int main(){
    while(scanf("%d%d%d",&n,&m,&k) == 3 && n){
        printf("%lld\n",((solve(n,k) - solve(m-1,k)) % mod + mod) % mod);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值