线性筛素数
int pj[N], cnt, vis[N];
void get_primes(int n) {
for(int i = 2; i <= n; i ++) {
if(!vis[i]) pj[++ cnt] = i;
for(int j = 1; pj[j] * i <= n; j ++) {
vis[pj[j] * i] = true;
if( i % pj[j] == 0) break;
}
}
}
唯一分解定理:(x = prod p_{i} ^ {a_{i}})
约数个数定理: (prod (a_i + 1))
约数个数和定理: (prodsum_{i = 0}^{a_i}p_j^i)
最大公约数: (gcd(a,b) = gcd(b, a mod b))
线性筛phi
(φ(n) = N prod (1 - frac{1}{p_i}))
void get_primes(int n) {
phi[1] = 1;
for(int i = 2; i <= n; i ++) {
if(!vis[i]) primes[++ cnt] = i, phi[i] = i - 1;
for(int j = 1; primes[j] * i <= n; j ++) {
vis[primes[j] * i] = true;
if( i % primes[j] == 0) {
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
欧拉定理:
其中,((a,n)=1)
证明:
引入一个概念: 缩系
同时乘上a, 也是mod n 的一个缩系,因为乘上a之后,两两互不同余,并且仍然与n互质。
即
两边约去公因数,得到欧拉定理。
如果 (n)为素数, 可以得到 费马小定理:
由此,可以得到
所以,如果模数是素数,那么a的逆元就是(a^{p-2}),可以用快速幂求。
扩展欧几里得exgcd:
exgcd求解方程
由于
所以,我们可以写成和上面的方程一样的形式:
进一步化简
所以
通解
代码:
void exgcd(int& x, int& y, int a, int b) {
if(!b) {
x = 1, y = 0;
return;
}
exgcd(y, x, b, a % b);
y -= (a / b) * x;
}
中国剩余定理
给定 (2n) 个整数(a_1,a_2,…,a_n)和(m_1,m_2,…,m_n,)求一个最小的非负整数 (x),满足(∀i∈[1,n],x≡m_i(mod a_i))并且模数两两互质。
首先,设
求解
for(int i = 1; i <= n; i ++) {
scanf("%lld%lld", &mo[i], &re[i]);
M *= mo[i];
}
for(int i = 1; i <= n; i ++) {
m[i] = M / mo[i];
LL tmp;
exgcd(inv[i], tmp, m[i], mo[i]);
}
LL ans = 0;
for(int i = 1; i <= n; i ++) {
ans += (re[i] * m[i] * inv[i]);
}
printf("%lld
", (ans % M + M) % M);
扩展中国剩余定理exCRT
首先考虑两个方程,
(x ≡ a_1(mod m_1))
(x ≡ a_2(mod m_2))
进一步,写成等式:
(x = k_1 * m_1 + a_1)
(x = k_2 *m_2 + a_2)
两式相等,得到:
(k_1 *m_1-k_2*m_2 = a_1-a_2)
只有(k_1)与(k_2)是未知数,可以用exgcd进行求解。
令(d = gcd(m_1, m_2)),那么,(k_1)的通解为(k_t = k_1 + t * frac{m_2}{gcd(m_1,m_2)})
所以(x = k_t * m_1 +a_1 = t * frac{m_1m_2}{d} +k_1*m_1+a_1)
所以,我们将两个方程合并为一个方程,我们重新令(m_1 = lcm(m_1,m_2), k_1 = k_1*m_1 +a_1)
------------恢复内容开始------------
线性筛素数
int pj[N], cnt, vis[N];
void get_primes(int n) {
for(int i = 2; i <= n; i ++) {
if(!vis[i]) pj[++ cnt] = i;
for(int j = 1; pj[j] * i <= n; j ++) {
vis[pj[j] * i] = true;
if( i % pj[j] == 0) break;
}
}
}
唯一分解定理:(x = prod p_{i} ^ {a_{i}})
约数个数定理: (prod (a_i + 1))
约数个数和定理: (prodsum_{i = 0}^{a_i}p_j^i)
最大公约数: (gcd(a,b) = gcd(b, a mod b))
线性筛phi
(φ(n) = N prod (1 - frac{1}{p_i}))
void get_primes(int n) {
phi[1] = 1;
for(int i = 2; i <= n; i ++) {
if(!vis[i]) primes[++ cnt] = i, phi[i] = i - 1;
for(int j = 1; primes[j] * i <= n; j ++) {
vis[primes[j] * i] = true;
if( i % primes[j] == 0) {
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
欧拉定理:
其中,((a,n)=1)
证明:
引入一个概念: 缩系
同时乘上a, 也是mod n 的一个缩系,因为乘上a之后,两两互不同余,并且仍然与n互质。
即
两边约去公因数,得到欧拉定理。
如果 (n)为素数, 可以得到 费马小定理:
由此,可以得到
所以,如果模数是素数,那么a的逆元就是(a^{p-2}),可以用快速幂求。
扩展欧几里得exgcd:
exgcd求解方程
由于
所以,我们可以写成和上面的方程一样的形式:
进一步化简
所以
通解
代码:
void exgcd(int& x, int& y, int a, int b) {
if(!b) {
x = 1, y = 0;
return;
}
exgcd(y, x, b, a % b);
y -= (a / b) * x;
}
中国剩余定理
给定 (2n) 个整数(a_1,a_2,…,a_n)和(m_1,m_2,…,m_n,)求一个最小的非负整数 (x),满足(∀i∈[1,n],x≡m_i(mod a_i))并且模数两两互质。
首先,设
求解
for(int i = 1; i <= n; i ++) {
scanf("%lld%lld", &mo[i], &re[i]);
M *= mo[i];
}
for(int i = 1; i <= n; i ++) {
m[i] = M / mo[i];
LL tmp;
exgcd(inv[i], tmp, m[i], mo[i]);
}
LL ans = 0;
for(int i = 1; i <= n; i ++) {
ans += (re[i] * m[i] * inv[i]);
}
printf("%lld
", (ans % M + M) % M);
扩展中国剩余定理exCRT
首先考虑两个方程,
(x ≡ a_1(mod m_1))
(x ≡ a_2(mod m_2))
进一步,写成等式:
(x = k_1 * m_1 + a_1)
(x = k_2 *m_2 + a_2)
两式相等,得到:
(k_1 *m_1-k_2*m_2 = a_1-a_2)
只有(k_1)与(k_2)是未知数,可以用exgcd进行求解。
令(d = gcd(m_1, m_2)),那么,(k_1)的通解为(k_t = k_1 + t * frac{m_2}{gcd(m_1,m_2)})
所以(x = k_t * m_1 +a_1 = t * frac{m_1m_2}{d} +k_1*m_1+a_1)
所以,我们将两个方程合并为一个方程,我们重新令(m_1 = lcm(m_1,m_2), k_1 = k_1*m_1 +a_1)
scanf("%lld%lld", &m1, &a1);
n --;
LL x, m;
while(n --) {
scanf("%lld%lld", &m2, &a2);
LL k1, k2;
LL d = exgcd(k1, k2, m1, m2);
if( (a2 - a1) % d) {
x = -1;
break;
}
k1 *= (a2 - a1) / d;//注意,这里是a2 - a1,不是a1 - a2,这里要跟exgcd的系数对应。
k1 = (k1 % (m2 / d) + m2 / d`) % (m2 / d);
x = k1 * m1 + a1;
a1 = k1 * m1 + a1;
m = m1 / d * m2;
m1 = m;
}
if( x != -1)
x = (x % m + m) % m;
组合计数
组合数递推:
void Com(int n, int m) {
C[0][0] = 1;
for(int i = 1; i <= n; i ++) {
C[i][0] = 1;
for(int j = 1; j <= m; j ++)
C[i][j] = (C[i - 1][j] % p + C[i - 1][j - 1] % p) % p;
}
}
fact[0] = infact[0] = 1;
for(int i = 1; i < N - 99; i ++) {
fact[i] = (LL)fact[i - 1] * i % p;
infact[i] = (LL)infact[i - 1] * qmi(i, p - 2) % p;
}
Lucas定理:
复杂度(O(p + log_pn))
LL C(LL a, LL b, LL p) {
return fac[a] * inv[b] % p * inv[a - b] % p;
}
LL Lucas(LL a, LL b, LL p)
{
if( a < p && b < p)
return C(a, b, p);
return Lucas(a / p, b / p, p) * Lucas(a % p, b % p, p) % p;
}