HDU 3944 DP? 【組合數取模+階乘預處理】
阿新 • • 發佈:2019-02-03
題意:從楊輝三角頂部走到的第n行第m列有很多種走法,求出這些走法中所經過的數之和的最小值。
首先稍加分析得出答案的組合表示式
C(n+1,m+1,p)+m (mod p) 這是在2m>n時的結果(若不是變換一下m=n-m)
然後就是套用模板實現,首先p是每組資料不同,但是p<10^4,素數非常有限,為避免超時可以先打一個素數表(得出這個範圍內的素數不超過1300個),然後針對每個素數算出階乘表與逆元表。
上述辦法在空間複雜度上將將好,程式碼如下:
#include<stdio.h> #include<string.h> typedef long long LL; LL fac[10005][10005]; //階乘表 LL inve[10005][10005]; //素數表 int p[1300],num=0; int vis[10005]; void cpnl() // cpnl means creat prime number list { memset(vis,0,sizeof(vis)); for(int i=2;i<=10000;i++) if (!vis[i]) { p[num++]=i; for(int j=i;j<=10000;j+=i) vis[j]=1; } } LL qmod(LL a,LL b,LL mod) //快速冪 { LL ans=1; a=a%mod; while(b) { if(b&1==1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } LL inv(LL a,LL p) //求a在模p下的乘法逆元(p是素數) { return qmod(a,p-2,p); } void cal_table() //計算階乘表 { for(int n=0;n<num;n++) { fac[p[n]][0]=inve[p[n]][0]=1; for(int i=1;i<p[n];i++) { fac[p[n]][i]=fac[p[n]][i-1]*i%p[n]; inve[p[n]][i]=inv(fac[p[n]][i],p[n]); } } } LL C(LL n,LL m,LL p) //組合數 { if(n<0 || m<0 || m>n) return 0; if(n==m) return 1; return fac[p][n]*inve[p][m]%p*inve[p][n-m]%p; } LL Lucas(LL n, LL m,LL p) { if(m == 0) return 1; return C(n % p, m % p,p) * Lucas(n / p, m / p,p) % p; } int main() { cpnl(); cal_table(); LL n,m,p; int t=1; while(~scanf("%lld%lld%lld",&n,&m,&p)) { if(n-m>m) m=n-m; printf("Case #%d: %lld\n",t++,(Lucas(n+1,m+1,p)+m)%p); } return 0; }