原題鏈接https://www.lydsy.com/JudgeOnline/problem.php?id=3398
容易想到的一種\(dp\)就是:設\(dp[i][j]\)表示前\(i\)頭牛裏面有\(j\)頭牡牛的方案數,那麼轉移方程就是:
\[ dp[i][j]=dp[i-1][j]+dp[i-k][j-1] \]
這裏解釋一下爲什麼是\(dp[i-k][j-1]\)。因爲dp數組存的是方案數,而如果第i頭牛要是牡牛,那麼\(i-k\)~\(i-1\)頭牛都必須是牝牛,也就是隻有這一種方案。但如果由\(j-k\)後面的狀態轉移過來,比如\(j-k+1\),那麼根據方程,這個狀態包含了第\(j-k\)頭牛爲牡牛的情況,這是不合法的情況。又因爲\(j-k\)之前的牛是什麼牛都無所謂,所以應該從\(j-k\)的狀態轉移過來。
然後你發現這樣開數組會爆空間,時間也會爆。然而我們發現,第i頭牛是牡牛時,我們只需要知道第\(i-k\)頭牛不是牡牛的方案數即可,與前面有幾頭牡牛無關。那麼我們換一種狀態,設\(dp[i][0/1]\)表示第i頭牛是牝牛/牡牛的方案數。轉移方程類似:
\[ dp[i][0]=dp[i-1][0]+dp[i-1][1];\\ dp[i][1]=dp[i-k][0]; \]
時間複雜度爲\(O(N)\)
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100010
#define mod 5000011
using namespace std;
inline int read(){
register int x(0),f(1); register char c(getchar());
while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
long long dp[maxn][2];
int n,k;
int main(){
n=read(),k=read();
dp[0][0]=1;
for(register int i=1;i<=n;i++){
dp[i][0]=(dp[i-1][0]+dp[i-1][1])%mod;
dp[i][1]=dp[ max(0,i-k) ][0];
}
printf("%lld\n",(dp[n][0]+dp[n][1])%mod);
return 0;
}