1. 程式人生 > >HDU 3037 Saving Beans 多重集合的結合 lucas定理

HDU 3037 Saving Beans 多重集合的結合 lucas定理

memset init pan 逆元 lucas定理 scan name style ide

  題目鏈接: http://acm.hdu.edu.cn/showproblem.php?pid=3037

  題目描述: 要求求x1 + x2 + x3 + ...... + xn <= m 非負整數解的個數, 結果對P取模, 輸入的變量是n, m, p, P一定是素數

  解題思路: x1 + ... + xn = m 非負整數解的個數是C(n+m-1, n) , 所以答案就是 C(n+0-1, 0) + C(n+1-1, 1) + ...... C(n+m-1, n) 對P取模,

         由於組合數公式C(n, m) = C(n-1, m-1) + C(n-1, m) 所以該答案兩兩合並得C(n+m, n) , 又因為n, m 都非常大, 所以想到Lucas定理, 自己還不知道Lucas的原理, 健完身回來看

  代碼:

技術分享
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iterator>
#include <cmath>
#include <algorithm>
#include <stack>
#include <deque>
#include <map>
#define lson l, m, rt<<1
#define
rson m+1, r, rt<<1|1 #define mem0(a) memset(a,0,sizeof(a)) #define sca(x) scanf("%d",&x) #define de printf("=======\n") typedef long long ll; using namespace std; const int maxn = 100005; ll n, m, p; ll f[maxn]; void init( ll p ) { f[0] = 1; for( int i = 1; i <= p; i++ ) { f[i]
= f[i-1] * i % p; } } ll q_power(ll a, ll b, ll p) { ll ret = 1; while( b ) { if( b & 1 ) ret = ret * a % p; b >>= 1; a = a * a % p; } return ret % p; } ll lucas( ll n, ll m, ll p ) { ll ans = 1; while( n && m ) { ll nn = n % p, mm = m % p; if( nn < mm ) return 0; ans = ans * f[nn] * q_power(f[mm]*f[nn-mm]%p, p-2, p) % p; n /= p; m /= p; } return ans; } int main() { int t; sca(t); while( t-- ) { scanf( "%I64d%I64d%I64d", &n, &m, &p ); init(p); printf( "%I64d\n", lucas(n+m, n, p) ); } return 0; }
View Code

  思考: 自己一開始只做到了答案加和那一步, 以為答案是用到逆元呢, 結果一看n, m的取值範圍傻眼了, 其實化簡到那個式子應該能想到最終等式的.........沒化簡出來最終的等式是真的菜, 其實我感覺那個公式有點Dp 的意思噢.........lucas自己一直聽說今天終於了解了一些了, 繼續加油, 健身回來之後去了解Lucas的原理

HDU 3037 Saving Beans 多重集合的結合 lucas定理