線性篩求素數
int prime[MAXN]; // 儲存素數
bool is_not_prime[MAXN] = {1, 1}; // 0和1都不是素數
// 篩選 n 以內的所有素數
void xxs(int n) {
for (int i = 2; i <= n; ++i) {
if (!is_not_prime[i]) { // 如果i是素數
prime[++prime[0]] = i;
}
for (int j = 1; j <= prime[0] && i * prime[j] <= n; ++j) {
is_not_prime[i*prime[j]] = 1;
// 如果i中包含了該質因子,則停止
if (i % prime[j] == 0) break;
}
}
}
尤拉函式
求單個
int euler_phi(int n) {
// 如果存在大於根號n的質因子,至多有一個
int m = int(sqrt(n + 0.5));
int ans = n;
// 跟判定素數很像
for (int i = 2; i <= m; i++) {
// 如果i能整除n,則i是n的因子,也會質因子(看下面的while)
if (n % i == 0) {
ans = ans / i * (i - 1); // 根據定義計算
// 根據唯一分解定理,去掉i的冪
while (n % i == 0) n /= i;
}
}
// 如果最後n>1,則為一個大於根號n的一個質因子
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
求多個
// 整體框架為線性篩素數的程式碼,中間根據尤拉函式的性質加了幾條語句而已
int n, phi[N], prime[N], tot;
bool not_prime[N]; // true表示不是素數,false表示是素數
void getPhi() {
int i, j, k;
phi[1] = 1;
for (i = 2; i <= n; ++i) {
if (!not_prime[i]) {
prime[++tot] = i;
phi[i] = i-1; // 根據性質2
}
for (j = 1; j <= tot; ++j) {
k = i * prime[j];
if (k > n) break;
not_prime[k] = true;
if (i % prime[j] == 0) { // 變形1
phi[k] = prime[j] * phi[i];
break;
} else { // 變形2
phi[k] = (prime[j]-1) * phi[i];
}
}
}
}
gcd求最大公約數
// 遞迴形式
int gcd(int x, int y) {
return (y == 0 ? x : gcd(y, x%y));
}
// 非遞迴形式
int gcd(int x, int y) {
int r = x % y; // 取餘數
while (r) { // 餘數不為0,交換變數,繼續做除法
x = y;
y = r;
r = x % y;
}
return y; // 餘數為0時,除數為gcd
}
乘法逆元
費馬小定理求逆元
typedef long long ll;
ll quickpow(ll a, ll n, ll p) { //快速冪求 a^n % p
ll ans = 1;
while(n) {
if(n & 1) ans = ans * a % p;
a = a * a % p;
n >>= 1;
}
return ans;
}
ll niyuan(ll a, ll p) { //費馬小定理求逆元 a^(p-2)%p
return quickpow(a, p - 2, p);
}
線性求1-n逆元
// 因為 1<i<p,所以 p/i 一定小於 p
ny[1] = 1;
for (int i = 2; i < p; ++i) {
ny[i] = (long long)(p - p / i) * ny[p % i] % p; // 注意最後的模 p 不要忘記
}
中國剩餘定理
物不知數問題
LL CRT(int k, LL a[], LL r[]) {
LL n = 1, ans = 0;
for (int i = 1; i <= k; i++) n = n * r[i];
for (int i = 1; i <= k; i++) {
LL m = n / r[i], b, y;
exgcd(m, r[i], b, y); // b * m mod r[i] = 1
ans = (ans + a[i] * m * b % n) % n;
}
return (ans % n + n) % n;
}
盧卡斯定理
求大組合數取模
// 需要先預處理出fact[],即階乘
ll C(ll n, ll m, ll p) {
return n < m ? 0 : fact[n] * inv(fact[m], p) % p * inv(fact[n - m], p) % p;
}
ll Lucas(ll n, ll m, ll p) {
if (m == 0) return 1;
return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;
}
詳解網址
數論基礎