牡牛和牝牛
牡牛和牝牛
有n個0或者1,進行全排列,要求任意兩個0間至少有k個1,詢問其方案數%5000011。
對于全部數據,對于全部數據,\(1≤N≤10^5,0≤K<N\)。
解:
顯然為排列組合問題,考慮方向自然為通項與遞推方程。
法一(通項公式):
首先0決定了1的擺放,其次數據范圍支持對0的枚舉,于是枚舉0的個數,設其x,于是每個間隔至少要有k個1,不妨先構造讓其滿足條件,需要(x-1)k個1,顯然有\((x-1)k+x\leq n\Rightarrow [\frac{n+k}{k+1}]\),于是問題即變成對于剩下的1的插入0之間的間隔,
共有x+1個間隔,剩下的1的個數為\(n-(x-1)k-x\)。
思路一:
間隔是有序的,而1是無序的,不好解決無序的元素放入有序的盒子的方案數的問題,反過來看則是有序的間隔放入無序的1,而間隔可以多次放入同一個1,即可重組合,于是方案數不難得知為\(C_{n-(x-1)k}^{x}\)。
思路二:
插入組合問題很困難,于是考慮組合轉排列,轉換模型,即等價于\(n-(x-1)k-x\)個1與\(x\)個0進行全排列,根據可重排列公式有:
\[\frac{(n-(x-1)k-x+x)!}{x!(n-(x-1)k-x)!}=\frac{(n-(x-1)k)!}{x!(n-(x-1)k-x)!}\]
\[=C_{n-(x-1)k}^{x}\]
所以只要枚舉0的個數,代入公式累加即可。
參考代碼:
#include <iostream> #include <cstdio> #define il inline #define ri register #define ll long long #define yyb 5000011 using namespace std; ll jc[100001],jv[100001],lsy(1); il ll pow(ll,ll),c(int,int); int main(){int n,i,j,k,li;scanf("%d%d",&n,&k);for(i=jc[0]=1;i<=n;++i)jc[i]=jc[i-1]*i%yyb;jv[n]=pow(jc[n],yyb-2),li=(n+k)/(k+1);for(i=n,jv[0]=1;i>1;--i)jv[i-1]=jv[i]*i%yyb;for(i=1;i<=li;++i)(lsy+=c(n-(i-1)*k,i))%=yyb;printf("%lld",lsy);return 0; } il ll c(int n,int r){if(n<r)return 0;return jc[n]*jv[r]%yyb*jv[n-r]%yyb; } il ll pow(ll x,ll y){ll ans(1);while(y){if(y&1)ans=ans*x%yyb;x=x*x%yyb,y>>=1;}return ans; }法二(遞推方程)
經驗告訴我們以序列長度為狀態,于是設\(f[i]\)表示填到第i個位置的方案數,顯然策略為填0或者1,填1恒滿足累加\(f[i-1]\),填0導致前k個都不能填0,故累加\(f[i-k-1]\),于是有:
\[f[i]=f[i-1]+f[i-k-1]\]
參考代碼:
#include <iostream> #include <cstdio> #define il inline #define ri register #define yyb 5000011 using namespace std; int dp[100001]; int main(){int n,k,i;scanf("%d%d",&n,&k);for(i=1;i<=k+1;++i)dp[i]=i+1;for(i=k+2;i<=n;++i)dp[i]=(dp[i-k-1]+dp[i-1])%yyb;printf("%d",dp[n]);return 0; }轉載于:https://www.cnblogs.com/a1b3c7d9/p/10780306.html
總結
- 上一篇: python中 yield的用法和生成器
- 下一篇: HDU 6070 Dirt Ratio(