P2183 [国家集训队]礼物(扩展卢卡斯)
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2183 [国家集训队]礼物(扩展卢卡斯)相关的知识,希望对你有一定的参考价值。
题意:
有n个礼物,分给m个人,分给第i个人的礼物数量是wi,问送礼物的方案数。
题解:
扩展卢卡斯模板题
很容易看出和组合数有关的题目,对于总方案,完美可以将其分解为m个不同的方案数的乘积
比如样例1:4个礼物,2个人,第一个人要1个礼物,则第一个人取走礼物的方案为
C
4
1
C_{4}^{1}
C41,第二个人要2个礼物,方案为
C
3
2
C_{3}^{2}
C32,总方案为
C
4
1
∗
C
3
2
=
12
C_{4}^{1}*C_{3}^{2}=12
C41∗C32=12
现在改变顺序:第二个人先拿礼物,
C
4
2
C_{4}^{2}
C42,然后第一个人拿礼物,
C
2
1
C_{2}^{1}
C21,总方案为
C
4
2
∗
C
2
1
=
12
C_{4}^{2}*C_{2}^{1}=12
C42∗C21=12
完美可以发现,无论谁取走礼物,结果都是一样的,那我们只需要按照所给礼物顺序计算就行。
总方案为:
C
n
w
1
∗
C
n
−
w
1
w
2
∗
.
.
.
.
m
o
d
p
C_{n}^{w_{1}}*C_{n-w_{1}}^{w_{2}}*....\\bmod p
Cnw1∗Cn−w1w2∗....modp
本题的p不一定是质数,此时就没办法求组合数,就要用的扩展卢卡斯定理,该算法就是专门解决模数p不是质数的情况
代码:
#include <bits/stdc++.h>
#include <cstdio>
using namespace std;
#define FOR(i, a, b) for (ll i= a; i <= b; ++i)
#define DEC(i, a, b) for (ll i= a; i >= b; --i)
typedef long long ll;
ll mod;
void exgcd(ll a, ll b, ll& x, ll& y)
{
if (!b) {
x= 1, y= 0;
return;
}
exgcd(b, a % b, y, x);
y-= a / b * x;
return;
}
inline ll inv(ll n, ll p)
{
ll x, y;
exgcd(n, p, x, y);
return (x + p) % p;
}
ll qpow(ll base, ll p, ll mod)
{
ll ret= 1;
for (; p; p>>= 1, base= base * base % mod)
if (p & 1)
ret= ret * base % mod;
return ret;
}
ll CRT(int n, ll* a, ll* m)
{
ll M= 1, ret= 0;
FOR(i, 1, n) M*= m[i];
FOR(i, 1, n)
{
ll w= M / m[i];
ret= (ret + a[i] * w % mod * inv(w, m[i]) % mod) % mod;
}
return (ret + mod) % mod;
}
ll calc(ll n, ll q, ll qk)
{
if (!n)
return 1;
ll ret= 1;
FOR(i, 1, qk)
if (i % q)
ret= ret * i % qk;
ret= qpow(ret, n / qk, qk);
FOR(i, n / qk * qk + 1, n)
if (i % q)
ret= ret * (i % qk) % qk;
return ret * calc(n / q, q, qk) % qk;
}
ll multiLucas(ll n, ll m, ll q, ll qk)
{
int cnt= 0;
for (ll i= n; i; i/= q)
cnt+= i / q;
for (ll i= m; i; i/= q)
cnt-= i / q;
for (ll i= n - m; i; i/= q)
cnt-= i / q;
return qpow(q, cnt, qk) * calc(n, q, qk) % qk * inv(calc(m, q, qk), qk) % qk * inv(calc(n - m, q, qk), qk) % qk;
}
ll exLucas(ll n, ll m, ll p)
{
int cnt= 0;
ll qk[20], a[20]; //存放所有的 q^k 和待合并答案的结果
for (ll i= 2; i * i <= p; ++i) //质因数分解
{
if (p % i == 0) {
qk[++cnt]= 1;
while (p % i == 0)
qk[cnt]*= i, p/= i;
a[cnt]= multiLucas(n, m, i, qk[cnt]);
}
}
if (p > 1)
qk[++cnt]= p, a[cnt]= multiLucas(n, m, p, p);
return CRT(cnt, a, qk); //CRT 合并答案
}
int w[20];
int main()
{
ll n, m, p;
scanf("%lld %lld %lld", &p, &n, &m);
mod= p;
int sum= 0;
for (int i= 1; i <= m; i++)
cin >> w[i], sum+= w[i];
if (sum > n) {
printf("Impossible");
return 0;
}
ll ans= 1;
for (int i= 1; i <= m; i++) {
ans= ans * exLucas(n, w[i], mod) % mod;
n-= w[i];
}
printf("%lld\\n", ans);
return 0;
}
以上是关于P2183 [国家集训队]礼物(扩展卢卡斯)的主要内容,如果未能解决你的问题,请参考以下文章