HDU 3037 Saving Beans 多重集合的结合 lucas定理
Posted FriskyPuppy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 3037 Saving Beans 多重集合的结合 lucas定理相关的知识,希望对你有一定的参考价值。
题目链接: 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; }
思考: 自己一开始只做到了答案加和那一步, 以为答案是用到逆元呢, 结果一看n, m的取值范围傻眼了, 其实化简到那个式子应该能想到最终等式的.........没化简出来最终的等式是真的菜, 其实我感觉那个公式有点Dp 的意思噢.........lucas自己一直听说今天终于了解了一些了, 继续加油, 健身回来之后去了解Lucas的原理
以上是关于HDU 3037 Saving Beans 多重集合的结合 lucas定理的主要内容,如果未能解决你的问题,请参考以下文章