题意:
给n个士兵排队,每个士兵三种G、R、P可选,求至少有m个连续G士兵,最多有k个连续R士兵的排列的种数。
思路:
dp递推。
先把问题都转化成至多连续的情况:至多k个连续R,至多n个连续G情况 【减去】至多k个连续R,至多(m-1)个连续G情况。
//dp[i][0]表示第i个为G,至多有u个连续G,至多有v个连续R的个数 //这里的u和v固定
//dp[i][1]表示第i个为R,…
//d[i][2]表示第i个为P,…
当第i个为P的情况很好考虑不会对连续的R和G产生影响,dp[i][2]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
当第i个为G时
如果i<=u 时 无论怎么放都不会超过u个连续的G这个限制条件 所以
dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
如果i=u+1时,要排除前u个都放了G的情况,dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1;
如果i>u+1时,要排除从i-1到i-u位置都放了G的情况,dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-u-1][1]-dp[i-u-1][2];
当第i个为R时
如果i<=v 时 无论怎么放都不会超过u个连续的G这个限制条件 所以dp[i][1]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
如果i=v+1时,要排除前v个都放了G的情况,dp[i][1]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1;
如果i>v+1时,要排除从i-1到i-v位置都放了G的情况,dp[i][1]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-v-1][0]-dp[i-v-1][2];
注意最后的取模!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
#define first fi
#define second se
#define pii pair<int,int>
#define M 1000000007
using namespace std;
typedef long long LL;
const int maxn = 1000000+5;
LL d[maxn][3];
LL n,m,k,u,v;
LL dp(){
//初始化
d[0][0] = 1;
d[0][1] = 0;
d[0][2] = 0;
for(int i = 1; i <= n; ++i){
LL sum = (d[i-1][0] + d[i-1][1] + d[i-1][2]) % M;
//如果第i个位置
// 放P
d[i][2] = sum;
// 放G
if(i <= u) d[i][0] = sum;
else if(i == u+1) d[i][0] = (sum - 1) % M;
else d[i][0] = (sum - d[i-1-u][1] - d[i-1-u][2]) % M;
//放R
if(i <= v) d[i][1] = sum;
else if(i == v+1) d[i][1] = (sum - 1) % M;
else d[i][1] = (sum - d[i-1-v][0] - d[i-1-v][2]) % M;
}
return (d[n][0] + d[n][1] + d[n][2]) % M;
}
int main()
{
freopen("in.txt","r",stdin);
while(scanf("%lld %lld %lld",&n,&m,&k) == 3){
LL ans;
u = n, v = k;
ans = dp();
u = m - 1, v = k;
// ans = (ans - dp()) % M; 错误
// ans = ((ans - dp()) + M) % M; 错误
ans = ((ans - dp())%M + M) % M;
printf("%lld\n",ans);
}
return 0;
}