链接:
hdu 5446
http://acm.hdu.edu.cn/showproblem.php?pid=5446
题意:
给你三个数$n, m, k$
第二行是$k$个数,$p_1,p_2,p_3 \cdots p_k$
所有$p$的值不相同且p都是质数
求$C(n, m) \ \%\ (p_1*p_2*p_3* \cdots *p_k)$的值
范围:$1\leq m\leq n\leq 1e18,\ 1\leq k\leq 10,p_i\leq 1e5$,保证$p_1*p_2*p_3* \cdots *p_k \leq 1e18$
分析:
我们知道题目要求$C(n, m) \ \% \ (p_1*p_2*p_3* \cdots *p_k)$的值
其实这个就是中国剩余定理最后算出结果后的最后一步求余
那$C(n, m)$相当于以前我们需要用中国剩余定理求的值
然而$C(n, m)$太大,我们只好先算出$C(n,m) \ \% \ p_1 = r_1 \\ C(n,m) \ \% \ p_2 = r_2 \\ C(n,m) \ \% \ ; p_3 = r_3 \\ \vdots \\ C(n,m) \ \% \ p_k = r_k \\$
用$Lucas$,这些$r_1,r_2,r_3 \cdots r_k$可以算出来,然后又是用中国剩余定理求答案。
注意,有些地方直接乘会爆long long,按位乘可避免。
AC代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;;
typedef long long LL;
void gcd(LL a, LL b, LL &d, LL &x, LL &y)
{
if (!b) { d = a; x = 1; y = 0; }
else { gcd(b, a % b, d, y, x); y -= x * (a / b); }
}
LL quickmul(LL m, LL n, LL k)
{
m = (m % k + k) % k; n = (n % k + k) % k; //变成较小的正数
LL res = 0;
while (n > 0)
{
if (n & 1)
res = (res + m) % k;
m = (m + m) % k;
n = n >> 1;
}
return res;
}
//计算模n下a的逆。如果不存在逆,返回-1
//ax=1(mod n)
LL inv(LL a, LL n)
{
LL d, x, y;
gcd(a, n, d, x, y);
return d == 1 ? (x + n) % n : -1;
}
//n! % p
LL fact(LL n, LL p)
{
LL ret = 1;
for (int i = 1; i <= n; i++) ret = ret * i % p;
return ret;
}
LL comp(LL n, LL m, LL p)
{
if (n < 0 || m > n) return 0;
return fact(n, p) * inv(fact(m, p), p) % p * inv(fact(n - m, p), p) % p;
}
LL lucas(LL a, LL b, LL m)
{
LL ans = 1;
while (a && b)
{
ans = quickmul(ans, comp(a % m, b % m, m), m) % m;
a /= m; b /= m;
}
return ans;
}
//n个方程:x=a[i](mod m[i])
LL china(int n, LL* a, LL* m)
{
LL M = 1, d, y, x = 0;
for (int i = 0; i < n; i++) M *= m[i];
for (int i = 0; i < n; i++)
{
LL w = M / m[i];
gcd(m[i], w, d, d, y);
x = (x + quickmul(quickmul(w,y, M),a[i],M)) % M; //直接乘会爆long long,要用按位乘
}
return (x + M) % M;
}
int k;
LL n, m;
LL p[10 + 5],r[10 + 5];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
cin >> n >> m >> k;
for (int i = 0; i < k; i++)
cin >> p[i];
for (int i = 0; i < k; i++)
r[i] = lucas(n, m, p[i]);
LL ans = china(k, r, p);
cout << ans << endl;
}
return 0;
}
个性签名:时间会解决一切