题意:
约翰要带N(1≤N≤100000)只牛去参加集会里的展示活动,这些牛可以是牡牛,也可以是牝牛.
牛们要站成一排.但是牡牛是好斗的,
为了避免牡牛闹出乱子,约翰决定任意两只牡牛之间至少要有K(O≤K<N)只牝牛.
请计算一共有多少种排队的方法.所有牡牛可以看成是相同的,所有牝牛也一样.
答案对5000011取模。
数据范围:n<=1e5
解法:
解法1:
dp
令d(i,0/1)表示第i位放特殊牛/普通牛的方案数。
转移方程:
d(i,0)=d(i-k-1,0)+d(i-k-1,1)
d(i,1)=d(i-1,1)+d(i-1,0)
解法2:
组合数学
枚举选择的特殊牛的数量num,那么至少需要(num-1)*k只普通牛。
多出来的普通牛数量为t=n-num-(num-1)*k,这些插到num+1个普通牛堆里面,
这是球盒模型,n个相同的球放进m个不同的盒子里面,方案数为C(n+m-1,m-1),
带入一下数据,方案数为C(t+num,num)
需要转移的地方:因为t+num可能>1e5,因子阶乘预处理的范围要大一点
code(dp):
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
const int mod=5000011;
int d[maxm][2];
int n,k;
signed main(){
ios::sync_with_stdio(0);
cin>>n>>k;
d[1][0]=d[1][1]=1;//0是特殊牛,1是普通牛
for(int i=2;i<=n;i++){
if(i-k-1>=1){
d[i][0]=(d[i-k-1][0]+d[i-k-1][1])%mod;
d[i][1]=(d[i-1][0]+d[i-1][1])%mod;
}else{
d[i][0]=1;
d[i][1]=(d[i-1][0]+d[i-1][1])%mod;
}
}
int ans=(d[n][0]+d[n][1])%mod;
cout<<ans<<endl;
return 0;
}
code(组合数学):
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
const int mod=5000011;
int fac[maxm];
int n,k;
int ppow(int a,int b,int mod){
int ans=1%mod;a%=mod;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int C(int n,int m){
if(m<0||m>n)return 0;
return fac[n]*ppow(fac[m]*fac[n-m]%mod,mod-2,mod)%mod;
}
signed main(){
ios::sync_with_stdio(0);
fac[0]=1;
for(int i=1;i<maxm;i++)fac[i]=fac[i-1]*i%mod;
cin>>n>>k;
int ans=0;
for(int i=0;i<=n;i++){
int t=(i-1)*k;
int remain=n-i-t;
if(remain<0)break;
ans+=C(remain+i,i);
ans%=mod;
}
cout<<ans<<endl;
return 0;
}