ZOJ 3747 Attack on Titans 带限制的递推DP
总共有 个人,编号 ,三个种类 ,要求有连续不少于 个 种类的人,连续不多于 个 种类的人,问这 个人的有多少种组合方式?
思路参考 https://blog.csdn.net/wust_ZJX/article/details/46809951
难以处理至少问题,因此要把至少转换成至多,设事件 至多 个 ,至多 个 ,事件 至多 个 ,至多 个 ,则 至少 个 ,至多 个 ,即为最终答案。现在考虑至多 个 ,至多 个 ,令 分别表示位置 放种类 对应的组合数,令 , 则对于种类 而言:
(1) 时,仍在限制之内:
(2) 时,要减去 这一段为 类对应的组合数,有一种:
(3) 时,则要减去 这一段为 类对应的组合数:
对于种类 同理有:
(1) 时,仍在限制之内:
(2) 时,要减去 这一段为 类对应的组合数,有一种:
(3) 时,则要减去 这一段为 类对应的组合数:
由于 类不影响限制条件,因此有:
代码如下:
#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;
}