ZOJ 3747 Attack on Titans 带限制的递推DP

Attack on Titans ZOJ - 3747

总共有 n n n 个人,编号 1 ∼ n 1\sim n 1n,三个种类 A , B , C A,B,C A,B,C,要求有连续不少于 m m m A A A 种类的人,连续不多于 k k k B B B 种类的人,问这 n n n 个人的有多少种组合方式?

思路参考 https://blog.csdn.net/wust_ZJX/article/details/46809951

难以处理至少问题,因此要把至少转换成至多,设事件 D = { D=\{ D={ 至多 n n n A A A,至多 k k k B B B } \} },事件 E = { E=\{ E={ 至多 m − 1 m-1 m1 A A A,至多 k k k B B B } \} },则 D − E = { D-E=\{ DE={ 至少 m m m A A A,至多 k k k B   } B\ \} B },即为最终答案。现在考虑至多 u u u A A A,至多 v v v B B B,令 d p [ i ] [ 0 ] , d p [ i ] [ 1 ] , d p [ i ] [ 2 ] dp[i][0],dp[i][1],dp[i][2] dp[i][0],dp[i][1],dp[i][2] 分别表示位置 i i i 放种类 A , B , C A,B,C A,B,C 对应的组合数,令 s u m = d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 2 ] sum=dp[i-1][0]+dp[i-1][1]+dp[i-1][2] sum=dp[i1][0]+dp[i1][1]+dp[i1][2], 则对于种类 A A A 而言:

(1) i ≤ u i\le u iu 时,仍在限制之内:

d p [ i ] [ 0 ] = s u m dp[i][0]=sum dp[i][0]=sum

(2) i = u + 1 i=u+1 i=u+1 时,要减去 1 ∼ u 1\sim u 1u 这一段为 A A A 类对应的组合数,有一种:

d p [ i ] [ 0 ] = s u m − 1 dp[i][0]=sum-1 dp[i][0]=sum1

(3) i > u + 1 i>u+1 i>u+1 时,则要减去 i − u ∼ i − 1 i-u\sim i-1 iui1 这一段为 A A A 类对应的组合数:

d p [ i ] [ 0 ] = s u m − d p [ i − u − 1 ] [ 1 ] − d p [ i − u − 1 ] [ 2 ] dp[i][0]=sum-dp[i-u-1][1]-dp[i-u-1][2] dp[i][0]=sumdp[iu1][1]dp[iu1][2]

对于种类 B B B 同理有:

(1) i ≤ v i\le v iv 时,仍在限制之内:

d p [ i ] [ 1 ] = s u m dp[i][1]=sum dp[i][1]=sum

(2) i = v + 1 i=v+1 i=v+1 时,要减去 1 ∼ v 1\sim v 1v 这一段为 B B B 类对应的组合数,有一种:

d p [ i ] [ 1 ] = s u m − 1 dp[i][1]=sum-1 dp[i][1]=sum1

(3) i > v + 1 i>v+1 i>v+1 时,则要减去 i − v ∼ i − 1 i-v\sim i-1 ivi1 这一段为 B B B 类对应的组合数:

d p [ i ] [ 1 ] = s u m − d p [ i − v − 1 ] [ 0 ] − d p [ i − v − 1 ] [ 2 ] dp[i][1]=sum-dp[i-v-1][0]-dp[i-v-1][2] dp[i][1]=sumdp[iv1][0]dp[iv1][2]

由于 C C C 类不影响限制条件,因此有:

d p [ i ] [ 2 ] = s u m dp[i][2]=sum dp[i][2]=sum

代码如下:

#include<iostream>
#include<cstdio>
//#define WINE
#define MOD 1000000007
#define MAXN 1000005
using namespace std;
typedef long long ll;
ll n,m,k,dp[MAXN][3],sum;
ll f(ll u,ll v){
    dp[0][0]=dp[0][1]=0;dp[0][2]=1;
    for(int i=1;i<=n;i++){
        sum=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%MOD;
        if(i<=u)dp[i][0]=sum;
        else if(i==u+1)dp[i][0]=(sum-1+MOD)%MOD;
        else dp[i][0]=((sum-dp[i-u-1][1]-dp[i-u-1][2])%MOD+MOD)%MOD;
        if(i<=v)dp[i][1]=sum;
        else if(i==v+1)dp[i][1]=(sum-1+MOD)%MOD;
        else dp[i][1]=((sum-dp[i-v-1][0]-dp[i-v-1][2])%MOD+MOD)%MOD;
        dp[i][2]=sum;
    }
    return (dp[n][0]+dp[n][1]+dp[n][2])%MOD;
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    while(scanf("%lld%lld%lld",&n,&m,&k)!=EOF)
        printf("%lld\n",((f(n,k)-f(m-1,k))%MOD+MOD)%MOD);
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值