Attack on Titans
题目链接:ZOJ - 3747题意:选出N个士兵编号1~N, 一共三种士兵, G,R,P三种, 士兵总数是无限大;G士兵至少有M个连续地, R士兵至多有K个连续地, P士兵随便放没有限制;
现在求这么一个事件Q:至少M个连续地士兵G&&至多K个连续的士兵R;
可以看出两个限制条件是不统一的, 一个至多, 一个至少,现在我们将他的限制条件统一;
设事件A为:至多N个连续地士兵G&&至多K个连续地士兵R;
设事件B为:至多M-1个连续地士兵G&&至多K个连续地士兵R;
那么可知Q=A-B;所以我们只需分别求出A,B的数量即可间接地求出Q;
A, B是相通的, 均为至多X个连续地士兵G&&至多Y个连续地士兵R;求解方式是相同的;
数组dp[i][j]表示第i个位置放的是j士兵(j==0:G; j==1:R; j==2:P;);
sum=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];(sum表示前i-1个位置有几种情况);
对于P士兵, 由于是随便放, 没有限制条件, 所以在i位置可以放P而不需要考虑非法情况;
所以 dp[i][2]=sum;
对于G士兵,:
当i <= X: 此时前边最多X-1个G, 第i个位置可以放G, 且不会有其他非法情况;
dp[i][0]=sum;
当i == X+1: 只有当1~i-1全为G, 即G有连续X个时, i位置不能放G, 其他情况均可;
dp[i][0]=sum-1;
当i > X+1: 当i-X~i-1全为G, 既有连续X个G, 位置不能放G, 此时i-X-1位置一定为P或R
dp[i][0]=sum-dp[i-X-1][1]-dp[i-X-1][2];
现在G士兵已放完, 下面放R士兵, 其实道理与G相同, 把X换成Y;
对于R士兵:
当i <= Y: 此时前边最多Y-1个R, 第i个位置可以放R, 且不会有其他非法情况;
dp[i][1]=sum;
当i == Y+1: 只有当1~i-1全为R, 即R有连续Y个时, i位置不能放R, 其他情况均可;
dp[i][1]=sum-1;
当i > Y+1: 当i-Y~i-1全为R, 既有连续Y个R, 位置不能放R, 此时i-Y-1位置一定为P或G
dp[i][1]=sum-dp[i-Y-1][0]-dp[i-Y-1][2];
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
int N, M, K;
const long long mod = 1e9+7;
const int maxn = 1e6+10;
long long dp[maxn][5];
long long ans(int m, int k){
long long sum=0;
dp[0][0]=1;
dp[0][1]=dp[0][2]=0;
for(int i=1; i<=N; i++){
sum=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod;
//i处放P士兵;
dp[i][2]=sum;
//i处放G士兵;
if(i <= m) dp[i][0]=sum;
if(i == m+1) dp[i][0]=(sum-1+mod)%mod;
if(i > m+1) dp[i][0]=(sum-dp[i-m-1][1]-dp[i-m-1][2]+mod)%mod;
//i处放R士兵;
if(i <= k) dp[i][1]=sum;
if(i == k+1) dp[i][1]=(sum-1+mod)%mod;
if(i > k+1) dp[i][1]=(sum-dp[i-k-1][0]-dp[i-k-1][2]+mod)%mod;
}
return (dp[N][0]+dp[N][1]+dp[N][2])%mod;
}
int main(){
while(~scanf("%d%d%d", &N, &M, &K)){
printf("%lld\n", ((ans(N, K)-ans(M-1, K))%mod+mod)%mod);
}
return 0;
}