本文主要是介绍hdu 3037 Skiing,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
组合数学-大组合数取模(Lucas定理)
题意:
将不超过m颗的相同的豆子放在n棵不同的树上,每棵树可以为空,求方案数mod p
(1 <= n, m <= 1000000000, 1 < p < 100000,p是质数)
分析:
可以理解为有m颗豆子,在n棵树上放k颗,然后再加一棵树,放m-k颗,于是变成了m颗相同的豆子放在n+1棵不同树上的方案数。
也就是求a[1]+a[2]+a[3]+......+a[n+1]=m,(a[i]>=0)的方案数,但是这种情况并不好计算,我们可以让每一份都加上1,令b[i]=a[i]+1>=1, 则b[1]+b[2]+b[3]+......b[n+1]=m+n+1,
可以用插板法了,m+n+1个元素有m+n个空,分成n+1份就是插n个板子,所以答案就是C(n+m,m) %p
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;const int MAX = 100005;
typedef long long ll; ll f[MAX]; ll mul(ll x, ll y, ll p)
{ ll res = 0; while(y) { if(y&1) res = (res+x) % p; x = (x<<1) % p; y >>= 1; } return res;
}
void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{ if (!b) { d = a; x = 1; y = 0; } else { exgcd(b, a%b, d, y, x); y -= a/b * x; }
}
void initp(int p)
{ f[0] = f[1] = 1; for(int i = 2; i < p; i++)f[i] = (f[i-1]*i) % p;
} ll comb(ll n, ll m, ll p)
{ if(m > n) return 0; ll ans = f[n]; ll g, x, y; exgcd(f[m], p, g, x, y); ans = mul(ans, (x%p)%p+p, p); exgcd(f[n-m], p, g, x, y); ans = mul(ans, (x%p)%p+p, p); return ans;
} //n取m,如果可能m>n请特判return 0;
ll lucas(ll n, ll m, int p)
{ ll ans = 1; initp(p); if(m > n) return 0; while(n && m && ans) { ans = mul(comb(n%p, m%p, p), ans, p); n /= p; m /= p; } return ans;
}
void work()
{ ll n, m, p, ans; scanf("%I64d%I64d%I64d", &n, &m, &p); ans = lucas(n + m, m, p); printf("%I64d\n", ans);
}
int main()
{ int T; scanf("%d",&T); while(T--) work(); return 0;
}
这篇关于hdu 3037 Skiing的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!