Educational Codeforces Round 83 (Rated for Div. 2)的D題所用到的Lucas定理,小編看了多篇博文後總結了一下,他其實是求組合數的一種方法,求組合數總共有以下幾種方法。
1.當數據範圍較小時,差不多1000左右,直接用遞歸打表求,這裏用到原理就是從n個球取m個球,相當於(取第n個,然後從前n-1個再取m-1個 + 不取第n個,從前n-1個取m個)即c[i][j] = (c[i - 1][j] + c[i - 1][j - 1])
for (int i = 0; i < N; i ++ )
for (int j = 0; j <= i; j ++ )
if (!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
2.當數據範圍中等,差不多是10e5 ,可以用逆元求解,爲什麼能用逆元求解,小編懶得碼字了,大家百度一下就行,原理也很簡單。
預處理出所有階乘取模的餘數fact[N]和所有階乘取模的逆元infact[N],這裏用到的公式就是C(a,b)=a! / b!(a-b)! ,這樣的話相當於a! * 逆元。,當p是質數的情況下直接用費馬小定理求解。
int qmi(int a, int k, int p) // 快速冪模板
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
// 預處理階乘的餘數和階乘逆元的餘數
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ )
{
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
//結果=fact[a]*infact[b]
當數據範圍到1e9的時候,這時候就用到Lucas定理了。
內容:
C(n, m) = C(n % p, m % p) * C(n / p, m / p) (mod p)
別問怎麼證,問就是不會,代碼也簡單 y總板子
這裏值得注意的就是,mod同樣不能太大
//若p是質數,則對於任意整數 1 <= m <= n,有:
// C(n, m) = C(n % p, m % p) * C(n / p, m / p) (mod p)
int qmi(int a, int k) // 快速冪模板
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b) // 通過定理求組合數C(a, b)
{
int res = 1;
for (int i = 1, j = a; i <= b; i ++, j -- )
{
res = (LL)res * j % p;
res = (LL)res * qmi(i, p - 2) % p;
}
return res;
}
int lucas(LL a, LL b)
{
if (a < p && b < p) return C(a, b);
return (LL)C(a % p, b % p) * lucas(a / p, b / p) % p;
}
上面都是取模一個數,假如不取模的時候還要去求C(n,m)時候,就用到了分解質因數法求組合數.板子還是用y總的,鏈接見上面。
/*當我們需要求出組合數的真實值,而非對某個數的餘數時,分解質因數的方式比較好用:
1. 篩法求出範圍內的所有質數
2. 通過 C(a, b) = a! / b! / (a - b)! 這個公式求出每個質因子的次數。 n! 中p的次數是 n / p + n / p^2 + n / p^3 + ...
3. 用高精度乘法將所有質因子相乘*/
int primes[N], cnt; // 存儲所有質數
int sum[N]; // 存儲每個質數的次數
bool st[N]; // 存儲每個數是否已被篩掉
void get_primes(int n) // 線性篩法求素數
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int get(int n, int p) // 求n!中的次數
{
int res = 0;
while (n)
{
res += n / p;
n /= p;
}
return res;
}
vector<int> mul(vector<int> a, int b) // 高精度乘低精度模板
{
vector<int> c;
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while (t)
{
c.push_back(t % 10);
t /= 10;
}
return c;
}
get_primes(a); // 預處理範圍內的所有質數
for (int i = 0; i < cnt; i ++ ) // 求每個質因數的次數
{
int p = primes[i];
sum[i] = get(a, p) - get(b, p) - get(a - b, p);
}
vector<int> res;
res.push_back(1);
for (int i = 0; i < cnt; i ++ ) // 用高精度乘法將所有質因子相乘
for (int j = 0; j < sum[i]; j ++ )
res = mul(res, primes[i]);
總的來說,Lucas定理還是挺簡單的,一學就會一用就廢.