數論
模運算
-
a m o d b = a ? ? a / b ? × b a\ mod \ b = a - \lfloor a / b \rfloor \times b a?mod?b=a??a/b?×b
-
n m o d p n \ mod\ p n?mod?p得到的結果的正負至于被除數 n n n有關
模運算的性質:
( a + b ) m o d m = ( ( a m o d m ) + ( b m o d m ) ) m o d m (a + b)\ mod\ m = ((a\ mod\ m) + (b\ mod\ m))\ mod\ m (a+b)?mod?m=((a?mod?m)+(b?mod?m))?mod?m
( a ? b ) m o d m = ( ( a m o d m ) ? ( b m o d m ) ) m o d m (a - b)\ mod\ m = ((a\ mod\ m) - (b\ mod\ m))\ mod\ m (a?b)?mod?m=((a?mod?m)?(b?mod?m))?mod?m = ( ( a m o d m ) ? ( b m o d m ) + m ) m o d m ((a\ mod\ m) - (b\ mod\ m) + m)\ mod\ m ((a?mod?m)?(b?mod?m)+m)?mod?m
( a × b ) m o d m = ( ( a m o d m ) × ( b m o d m ) ) m o d m (a \times b)\ mod\ m = ((a\ mod\ m) \times (b\ mod\ m))\ mod\ m (a×b)?mod?m=((a?mod?m)×(b?mod?m))?mod?m
但是除法例外,除法的取模需要用到逆元。
計算減法的時候,通常需要加上模數,防止出現負數。
快速冪
快速求解 a b m o d p a^b \ mod \ p ab?mod?p,可以采用二進制拼湊的思想
對于 b b b,它可以看成一個二進制數,可以寫成 2 2 2的冪相加的形式,那么 a b a^b ab可以寫成 a ( 2 i + 2 j + . . . + 2 k ) = a 2 i × a 2 j × . . . × a 2 k ( i , j , k ∈ S , 其中集合 S 為 b 轉換成二進制后位置上是 1 的位置 ) a^{(2^{i} + 2^j + ... + 2^k)} = a^{2^i}\times a^{2^j} \times ... \times a^{2^k} \ (i, j, k \in S,其中集合S為b轉換成二進制后位置上是1的位置) a(2i+2j+...+2k)=a2i×a2j×...×a2k?(i,j,k∈S,其中集合S為b轉換成二進制后位置上是1的位置)
我們只需計算的同時預處理每一項 a 2 i a^{2^i} a2i即可
using ll = long long;
ll qmi(ll a, ll b, ll c){ll res = 1;while(b){if(b & 1 == 1) res = res * a % c;a = a * a % c;b >>= 1;}return res;
}
乘法逆元
若 a × x ≡ 1 ( m o d b ) a \times x \equiv 1 \ (mod \ b) a×x≡1?(mod?b),且 a a a與 b b b互質,我們定義 x x x為 a a a的逆元,記為 a ? 1 a^{-1} a?1, x x x可稱為 a a a在模 b b b意義下的倒數
對于 a b m o d p \frac{a}{b} \ mod \ p ba??mod?p,我們可以求出 b b b在 m o d p mod \ p mod?p意義下的逆元,然后乘上 a a a,再 m o d p mod \ p mod?p即可
注意對于數 a a a在模 p p p意義下的乘法逆元,我們需要確保 a a a與 p p p互質,此時才有 a a a的乘法逆元 a ? 1 a^{-1} a?1,此條件等價于 p p p是質數
費馬小定理
a p ? 1 ≡ 1 ( m o d p ) a^{p - 1} \equiv 1 \ (mod \ p) ap?1≡1?(mod?p),其中 p p p是素數
費馬小定理求逆元
a × a p ? 2 ≡ 1 ( m o d p ) a \times a^{p - 2} \equiv 1 \ (mod\ p) a×ap?2≡1?(mod?p),根據乘法逆元的定義可知,此時 a a a的逆元是 a p ? 2 a^{p - 2} ap?2
ll inv(ll a, ll p){return qmi(a, p - 2, p);
}
擴展歐幾里得求逆元
根據逆元的定義 a x ≡ 1 ( m o d p ) ax \equiv 1 \ (mod \ p) ax≡1?(mod?p),我們得到方程: a x ? 1 = y p ax - 1 = yp ax?1=yp( a x ? 1 ax - 1 ax?1 是 p p p的倍數),則 a x + p y = 1 ax + py = 1 ax+py=1
解方程得 x m o d p x \ mod \ p x?mod?p得到的就是 a a a的乘法逆元,同時逆元存在的條件是 g c d ( a , p ) = 1 gcd(a, p) = 1 gcd(a,p)=1
求階乘的乘法逆元
同余
兩個整數 a a a和 b b b對用一個正整數 m m m取模后余數相同,則稱 a a a和 b b b對模 m m m同余,記作 a ≡ b ( m o d m ) a \equiv b \ (mod\ m) a≡b?(mod?m),這意味著 m ∣ ( a ? b ) m\ | \ (a - b) m?∣?(a?b)
同余的性質:
- 自反性: a ≡ a ( m o d m ) a \equiv a \ (mod\ m) a≡a?(mod?m)
- 對稱性:若 a ≡ b ( m o d m ) a \equiv b \ (mod \ m) a≡b?(mod?m),則 b ≡ a ( m o d m ) b \equiv a \ (mod \ m) b≡a?(mod?m)
- 傳遞性:若 a ≡ b ( m o d m ) , b ≡ c ( m o d m ) a \equiv b \ (mod\ m), b \equiv c \ (mod\ m) a≡b?(mod?m),b≡c?(mod?m),則 a ≡ c ( m o d m ) a \equiv c \ (mod\ m) a≡c?(mod?m)
- 同加性:若 a ≡ b ( m o d m ) a \equiv b \ (mod\ m) a≡b?(mod?m),則 a ± c ≡ b ± c ( m o d m ) a \pm c \equiv b \pm c \ (mod\ m) a±c≡b±c?(mod?m)
- 同乘性:若 a ≡ b ( m o d m ) a \equiv b \ (mod\ m) a≡b?(mod?m),則 a × c ≡ b × c ( m o d m ) a \times c \equiv b \times c \ (mod\ m) a×c≡b×c?(mod?m),若 a ≡ b ( m o d m ) , c ≡ d ( m o d m ) a \equiv b \ (mod \ m), c \equiv d \ (mod \ m) a≡b?(mod?m),c≡d?(mod?m),則 a × c ≡ b × d ( m o d m ) a \times c \equiv b \times d \ (mod\ m) a×c≡b×d?(mod?m)
- 同冪性:若 a ≡ b ( m o d m ) a \equiv b \ (mod \ m) a≡b?(mod?m),則 a c ≡ b c ( m o d m ) a^c \equiv b^c \ (mod\ m) ac≡bc?(mod?m)
- 不滿足同除性,但是有,若 c a ≡ c b ( m o d m ) ca \equiv cb \ (mod \ m) ca≡cb?(mod?m),則 a ≡ b ( m o d m g c d ( m , c ) ) a \equiv b \ (\ mod \ \frac{m}{gcd(m, c)}) a≡b?(?mod?gcd(m,c)m?)
擴展歐幾里得定理
對于給定的兩個整數 a a a, b b b,必須存在整數 x x x與 y y y,使得 a × x + b × y = g c d ( a , b ) a \times x + b \times y = gcd(a, b) a×x+b×y=gcd(a,b)
裴蜀定理
方程 a × x + b × y = c a \times x + b \times y = c a×x+b×y=c有整數解的充分必要條件是 c c c是 g c d ( a , b ) gcd(a, b) gcd(a,b)的倍數
證明:
-
必要性:令 p = g c d ( a , b ) p = gcd(a, b) p=gcd(a,b),則有 a = p × a ′ a = p \times a' a=p×a′, b = p × b ′ b = p \times b' b=p×b′;則 c = p × ( a ′ x + b ′ y ) c = p \times (a'x + b'y) c=p×(a′x+b′y),即 g c d ( a , b ) gcd(a, b) gcd(a,b)的倍數。
-
充分性:考慮 g c d gcd gcd的歐幾里得算法。 a , b a, b a,b通過 b , a % b b, a \% b b,a%b的方式一直輾轉下去,最后會出現 p , 0 p, 0 p,0的形式。原因是 0 ≤ a % b ≤ b ? 1 0 \leq a \% b \leq b - 1 0≤a%b≤b?1。對于遞歸出口 p , 0 p, 0 p,0,方程 a × p + 0 × y = c a \times p + 0 \times y = c a×p+0×y=c是否存在解呢?顯然有解,由于充分性,這里 a a a取 c p \frac{c}{p} pc?即可。那么輾轉相除過程中,下面的成立能否推得上面的成立呢?
-
對于方程 b x 1 + a % b × y 1 = c = a x + b y bx_1 + a \% b \times y_1 = c = ax + by bx1?+a%b×y1?=c=ax+by
-
左邊 = b x 1 + ( a ? ? a b ? × b ) × y 1 = a y 1 + b x 1 ? ? a b ? × b y 1 = a y 1 + b × ( x 1 ? ? a b ? × y 1 ) = 右邊 = a x + b y 左邊 = bx_1 + (a - \lfloor \frac{a}{b} \rfloor \times b) \times y_1 = ay_1 + bx_1 - \lfloor \frac{a}{b} \rfloor \times by_1 = ay_1 + b \times (x_1 - \lfloor \frac{a}{b} \rfloor \times y_1) = 右邊 = ax + by 左邊=bx1?+(a??ba??×b)×y1?=ay1?+bx1???ba??×by1?=ay1?+b×(x1???ba??×y1?)=右邊=ax+by
所以找到一組特解: x = y 1 x = y_1 x=y1?, y = x 1 ? ? a b ? × y 1 y = x_1 - \lfloor \frac{a}{b} \rfloor \times y_1 y=x1???ba??×y1?,就可以通過最下面的特解推得所有的特解。
-
對于特解 ( x 0 , y 0 ) (x_0, y_0) (x0?,y0?),設下一組解是 ( x 0 + d 1 , y 0 + d 2 ) (x_0 + d_1, y_0 + d_2) (x0?+d1?,y0?+d2?)
有 a × ( x 0 + d 1 ) + b × ( y 0 + d 2 ) = c a \times (x_0 + d_1) + b \times (y_0 + d_2) = c a×(x0?+d1?)+b×(y0?+d2?)=c,又, a × x 0 + b × y 0 = c a \times x_0 + b \times y_0 = c a×x0?+b×y0?=c,則 a d 1 + b d 2 = 0 ad_1 + bd_2 = 0 ad1?+bd2?=0
故, d 1 d 2 = ? b a = ? b / g c d ( a , b ) a / g c d ( a , b ) \frac{d_1}{d_2} = -\frac{b}{a} = -\frac{b / gcd(a, b)}{a / gcd(a, b)} d2?d1??=?ab?=?a/gcd(a,b)b/gcd(a,b)?
則 d 1 = b g c d ( a , b ) d_1 = \frac{b}{gcd(a, b)} d1?=gcd(a,b)b?, d 2 = ? a g c d ( a , b ) d_2 = -\frac{a}{gcd(a, b)} d2?=?gcd(a,b)a?,故一般解為:
x = x 0 + k × b g c d ( a , b ) x = x_0 + k \times \frac{b}{gcd(a, b)} x=x0?+k×gcd(a,b)b?, y = y 0 ? k × a g c d ( a , b ) ( k ∈ Z ) y = y_0 - k \times \frac{a}{gcd(a, b)} \ (k \in Z) y=y0??k×gcd(a,b)a??(k∈Z)
-
若要求 x x x的最小整數解,則可以通過 ( x 0 m o d ( b g c d ( a , b ) ) + b g c d ( a , b ) ) m o d x 0 (x_0 \ mod \ (\frac{b}{gcd(a, b)}) + \frac{b}{gcd(a, b)}) \ mod \ x_0 (x0??mod?(gcd(a,b)b?)+gcd(a,b)b?)?mod?x0?,原因是 x 0 x_0 x0?的周期是 b g c d ( a , b ) \frac{b}{gcd(a, b)} gcd(a,b)b?,所以可通過取模的方式得到結果
-
擴展歐幾里得算法
int exgcd(int a, int b, int &x, int &y){if(b == 0){x = 1, y= 0;return a;}int t = exgcd(b, a % b, y, x);y -= a / b * x;return t;
}
GCD
最大公約數:歐幾里得算法,時間復雜度 O ( l o g a ) O(log\ a) O(log?a)
g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a, b) = gcd(b, a\ mod\ b) gcd(a,b)=gcd(b,a?mod?b)
證明如下:
-
令 p = g c d ( a , b ) p = gcd(a, b) p=gcd(a,b),則有 a = p × a ′ a = p \times a' a=p×a′, b = p × b ′ b = p \times b' b=p×b′, g c d ( a ′ , b ′ ) = 1 gcd(a', b') = 1 gcd(a′,b′)=1
-
a m o d b = a ? ? a / b ? × b = p × a ′ ? p × b ′ × ? a / b ? a\ mod\ b = a - \lfloor a / b \rfloor \times b = p \times a' - p \times b' \times \lfloor a / b \rfloor a?mod?b=a??a/b?×b=p×a′?p×b′×?a/b?
-
可以看出, p ∣ b p \ | \ b p?∣?b, p ∣ ( a m o d b ) p \ | \ (a \ mod \ b) p?∣?(a?mod?b)
-
-
那么 p p p是否是最大公約數呢?即我們需要證明 g c d ( b ′ , a ′ ? b ′ × ? a / b ? ) = 1 gcd(b', a' - b' \times \lfloor a / b \rfloor) = 1 gcd(b′,a′?b′×?a/b?)=1
- 令 g c d ( b ′ , a ′ ? b ′ × ? a / b ? ) = d gcd(b', a' - b' \times \lfloor a / b \rfloor) = d gcd(b′,a′?b′×?a/b?)=d,則有 b ′ = d × b ′ ′ b' = d \times b'' b′=d×b′′, a ′ ? b ′ × ? a / b ? = d × c a' - b' \times \lfloor a / b \rfloor = d \times c a′?b′×?a/b?=d×c
- 轉換得到 a ′ = d × c + b ′ × ? a / b ? = d × c + d × b ′ ′ × ? a / b ? a' = d \times c + b' \times \lfloor a / b \rfloor = d \times c + d \times b'' \times \lfloor a / b \rfloor a′=d×c+b′×?a/b?=d×c+d×b′′×?a/b?
- 可以發現 d ∣ b ′ d\ |\ b' d?∣?b′, d ∣ a ′ d\ | \ a' d?∣?a′,與上述 g c d ( a ′ , b ′ ) = 1 gcd(a', b') = 1 gcd(a′,b′)=1矛盾,則命題得證。
歐拉函數
1... N 1...N 1...N中與 N N N互質的數的個數,被稱為歐拉函數,記作 ? ( N ) \phi(N) ?(N)
在唯一分解定理中, N = p 1 a 1 × p 2 a 2 × . . . × p m a m N = p_1^{a_1} \times p_2^{a_2} \times ...\times p_m^{a_m} N=p1a1??×p2a2??×...×pmam??,則 ? ( N ) = N × p 1 ? 1 p 1 × p 2 ? 1 p 2 × . . . × p m ? 1 p m = N × ( 1 ? 1 p 1 ) × ( 1 ? 1 p 2 ) × . . . × ( 1 ? 1 p m ) \phi(N) = N \times \frac{p_1 - 1}{p_1} \times \frac{p_2 - 1}{p_2} \times ... \times \frac{p_m - 1}{p_m} = N \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times...\times (1 - \frac{1}{p_m}) ?(N)=N×p1?p1??1?×p2?p2??1?×...×pm?pm??1?=N×(1?p1?1?)×(1?p2?1?)×...×(1?pm?1?)
證明:
先對 N N N分解質因數,得到 N = p 1 a 1 × p 2 a 2 × . . . × p m a m N = p_1^{a_1} \times p_2^{a_2} \times ...\times p_m^{a_m} N=p1a1??×p2a2??×...×pmam??
在 [ 1 , N ] [1, N] [1,N]中,我們先將所以 p 1 , p 2 , . . . , p m p_1, p_2, ..., p_m p1?,p2?,...,pm?的倍數減掉,再將所有 p i × p j p_i \times p_j pi?×pj?的倍數的數加上,再減去 p i × p j × p k p_i \times p_j \times p_k pi?×pj?×pk?的倍數的數,以此類推。最后剩下的就是與 N N N互質的數的個數。式子表達為 N ? ( N p 1 + N p 2 + . . . + N p m ) + ( N p 1 p 2 + N p 1 p 3 + . . . ) ? ( N p 1 p 2 p 3 + . . . ) . . . N - (\frac{N}{p_1} + \frac{N}{p_2} + ... + \frac{N}{p_m}) + (\frac{N}{p_1p_2} + \frac{N}{p_1p_3} + ... ) - (\frac{N}{p_1p_2p_3} + ...) ... N?(p1?N?+p2?N?+...+pm?N?)+(p1?p2?N?+p1?p3?N?+...)?(p1?p2?p3?N?+...)...
該式就是 N × ( 1 ? 1 p 1 ) × ( 1 ? 1 p 2 ) × . . . × ( 1 ? 1 p m ) N \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times...\times (1 - \frac{1}{p_m}) N×(1?p1?1?)×(1?p2?1?)×...×(1?pm?1?)的展開式
公式法求歐拉函數
#include <bits/stdc++.h>
using namespace std;void solve() {int n;cin >> n;auto f = [&](int x) -> int {int res = x;for (int i = 2; i <= x / i; i ++) {if (x % i != 0) continue;res = res / i * (i - 1);while (x % i == 0) x /= i;} if (x > 1) res = res / x * (x - 1); return res;};cout << f(n) << endl;
}int main() {int t;cin >> t;while (t --) {solve();}return 0;
}
篩法求歐拉函數
對于一個質數 p p p來說, ? ( p ) = p ? 1 \phi(p) = p - 1 ?(p)=p?1
對于每一個數 p r i m e s [ j ] × i primes[j] \times i primes[j]×i來說
- 當 i % p r i m e s [ j ] = 0 i \% primes[j] = 0 i%primes[j]=0時, i i i中的質因子包括 p r i m e s [ j ] primes[j] primes[j],則有 ? ( p r i m e s [ j ] × i ) = ? ( i ) × p r i m e s [ j ] \phi(primes[j] \times i) = \phi(i) \times primes[j] ?(primes[j]×i)=?(i)×primes[j]
- 當 i % p r i m e s [ j ] ≠ 0 i \% primes[j] \neq 0 i%primes[j]=0時, i × p r i m e s [ j ] i \times primes[j] i×primes[j]中的質因子既包括 i i i中的質因子,又包括 p r i m e s [ j ] primes[j] primes[j],則有 ? ( p r i m e s [ j ] × i ) = ? ( i ) × p r i m e s [ j ] × ( 1 ? 1 p r i m e s [ j ] ) \phi(primes[j] \times i) = \phi(i) \times primes[j] \times (1 - \frac{1}{primes[j]}) ?(primes[j]×i)=?(i)×primes[j]×(1?primes[j]1?)
#include <bits/stdc++.h>s
using namespace std;#define int long longconst int N = 1000010;
int cnt, primes[N];
bool st[N];
int phi[N];int getEuler(int n) {phi[1] = 1;for (int i = 2; i <= n; i ++) {if (st[i] == false) {primes[cnt ++] = i;phi[i] = i - 1;}for (int j = 0; j < cnt && primes[j] <= n / i; j ++) {st[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] / primes[j] * (primes[j] - 1);}}int res = 0;for (int i = 1; i <= n; i ++) {res += phi[i];}return res;
}signed main() {int n;cin >> n;cout << getEuler(n) << endl;return 0;
}
質數
分解質因數
給定一個數 x x x,由唯一分解定理可知, x = p 1 a 1 × p 2 a 2 × p 3 a 3 . . . ( p 1 < p 2 < p 3 ) x = p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3}...\ (p_1 < p_2 < p_3) x=p1a1??×p2a2??×p3a3??...?(p1?<p2?<p3?),我們從小到大枚舉每一個數,最先整除 x x x的除數 n n n一定是素數。同時將這個數中所有 n n n的因子除干凈,那么下次 x x x被整除時除數也是素數,從而分解了質因數。
#include <bits/stdc++.h>
using namespace std;int main() {int n;cin >> n;for (int i = 0; i < n; i ++) {int x;cin >> x;map<int, int> cnt;vector<int> res;for (int j = 2; j <= x / j; j ++) {if (x % j != 0) continue;res.push_back(j);while (x % j == 0) {cnt[j] ++;x /= j;}}if (x > 1) {res.push_back(x);cnt[x] ++;}for (auto& u : res) {cout << u << " " << cnt[u] << endl;}cout << endl;}return 0;
}
普通篩法
給定一個整數 x x x,并篩掉所有倍數,如 2 x , 3 x , 4 x . . . 2x, 3x, 4x... 2x,3x,4x...
篩完后, v i s [ i ] = f a l s e vis[i] = false vis[i]=false的為素數
這種方法的時間復雜度是 O ( n l o g n ) O(n\ log\ n) O(n?log?n)級別
對于每一個外層循環變量 i i i,內層循環枚舉所有不超過 n n n的 i i i的倍數,枚舉次數是 ? n i ? \lfloor \frac{n}{i} \rfloor ?in??,又 ? n i ? < n i \lfloor \frac{n}{i} \rfloor < \frac{n}{i} ?in??<in?,有調和級數的漸近公式 ∑ i = 1 n 1 i ≈ l n ( n + 1 ) + 0.577218 \sum^{n}_{i = 1} \frac{1}{i} \approx ln(n + 1) + 0.577218 ∑i=1n?i1?≈ln(n+1)+0.577218,其中 0.577218 0.577218 0.577218是歐拉常數
所以復雜度是 O ( n l o g n ) O(n\ log\ n) O(n?log?n)級別
雖然這個篩法的時間復雜度很差,但是調和級數枚舉引人深思,這種枚舉方式非常巧妙,而且復雜度僅有 O ( l o g n ) O(log\ n) O(log?n)級別
埃式篩法
在調和級數枚舉的篩法中,我們發現枚舉4的倍數無意義,因為從這里篩掉的數一定從2的倍數里篩掉了,所以我們想到:只用質數去篩
其復雜度只有 O ( n l o g l o g n ) O(n\ log\ log\ n) O(n?log?log?n)
#include <bits/stdc++.h>
using namespace std;const int N = 1e6 + 10;
int cnt, primes[N], n;
bool st[N];int main() {cin >> n;for (int i = 2; i <= n; i ++) {if (st[i]) continue;primes[cnt ++] = i;for (int j = 2 * i; j <= n; j += i) {st[j] = true;}}cout << cnt << endl;return 0;
}
歐拉篩
通過對埃氏篩法的分析,我們發現 6 = 2 × 3 6 = 2 \times 3 6=2×3會被質數 2 2 2和 3 3 3各篩一次。一個數有多少個質因子,它就會被篩多少次。那么我們能否讓一個數只被篩掉一次呢?
歐拉篩法可以做到并且每個數會被它的最小質因子篩掉
#include <bits/stdc++.h>
using namespace std;const int N = 1e6 + 10;
int cnt, primes[N], n;
bool st[N];int main() {cin >> n;for (int i = 2; i <= n; i ++) {if (!st[i]) primes[cnt ++] = i; //是素數for (int j = 0; j < cnt && primes[j] <= n / i; j ++) {st[primes[j] * i] = true;if (i % primes[j] == 0) break;}}cout << cnt << endl;return 0;
}
- 設 x = i × p r i m e s [ j ] x = i \times primes[j] x=i×primes[j]
- 若 i m o d p r i m e s [ j ] ≠ 0 i \ mod \ primes[j] \neq 0 i?mod?primes[j]=0,則 i i i中沒有 p r i m e s [ j ] primes[j] primes[j]這個質因子。又因為 j j j是從小到大遍歷的,所以 p r i m e s [ j ] < i primes[j] < i primes[j]<i,則 p r i m e s [ j ] primes[j] primes[j]是 x x x的最小質因子
- 若 i m o d p r i m e s [ j ] = 0 i \ mod \ primes[j] = 0 i?mod?primes[j]=0 ,說明 p r i m e s [ j ] primes[j] primes[j]是 i i i的一個質因子。又因為 j j j是從小到大遍歷的,所以 p r i m e s [ j ] primes[j] primes[j]是 i i i的最小質因子。所以 p r i m e s [ j ] primes[j] primes[j]是 x x x的最小質因子
- 綜上所述,每個數都是被其最小質因子 p r i m e s [ j ] primes[j] primes[j]篩掉且只會被篩掉一次
調和級數枚舉模型
內容參考于:[OI&ACM]調和級數枚舉倍數模型 - 知乎
給定一個數 n ( n ≤ 1 0 6 ) n\ (n \leq 10^6) n?(n≤106),求 [ 1 , n ] [1, n] [1,n]中所有整數 i i i的正因子個數
思路:枚舉 [ 1 , n ] [1, n] [1,n]中的所有數 i i i,對 i i i的所有倍數, i i i 會增加 1 1 1的貢獻。考慮調和級數枚舉:
int d[1000001] = {0}; // 儲存因數倍數,初始全為 0
for (int i = 1; i <= n; i++) // 枚舉 [1, n] 的數
{for (int j = i; j <= n; j += i) // 枚舉 i 的倍數{d[j]++;}
}
時間復雜度為 O ( n l o g n ) O(n \ log\ n) O(n?log?n)級別
下面的題目都應用了該模型,讀者不妨觀看思考
題目1:Problem - D - Codeforces
大意:給定一個長度為 n ( n ≤ 1 0 5 ) n\ (n \leq 10^5) n?(n≤105)的數組,每次操作可以任取一些位置上的數,并將該位置上的數染成黑色。然后將這些被染成黑色的位置索引的所有倍數染成綠色,每次操作的得分是該數組中黑色和綠色位置上的數的最大值。由于選擇染成黑色的操作有 2 n ? 1 2^n - 1 2n?1種,所以我們要求出所有操作的得分的和
選擇每個數所能得到的最大值是容易處理的。
vector<int> maxv(n + 1, 0);
for (int i = 1; i <= n; i ++) {for (int j = i; j <= n; j += i) {maxv[i] = max(maxv[i], a[j]);}
}
我們明確 m a x v maxv maxv數組的含義:選擇第 i i i上的數,將其涂成黑色,并將其索引的倍數涂成綠色,所能得到的最大值。假設有這樣的 m a x v maxv maxv數組, m a x v = [ 1 , 6 , 7 , 3 ] maxv = [1, 6, 7, 3] maxv=[1,6,7,3], 1 1 1貢獻了 1 1 1次, 3 3 3貢獻了 2 2 2次, 6 6 6貢獻了 4 4 4次, 7 7 7貢獻了 8 8 8次。這是因為:對于第一個位置上的數,我們有如下的選擇方式 [ 1 ] , [ 1 , 2 ] , [ 1 , 3 ] , [ 1 , 4 ] , [ 1 , 2 , 3 ] , [ 1 , 2 , 4 ] , [ 1 , 3 , 4 ] , [ 1 , 2 , 3 , 4 ] ( [ 1 , 3 , 4 ] 表示我們選擇第 1 , 3 , 4 個數字,其余同理 ) [1], [1, 2], [1, 3], [1, 4], [1, 2, 3],[1, 2, 4], [1, 3, 4], [1, 2, 3, 4]\ ([1, 3, 4]表示我們選擇第1,3,4個數字,其余同理) [1],[1,2],[1,3],[1,4],[1,2,3],[1,2,4],[1,3,4],[1,2,3,4]?([1,3,4]表示我們選擇第1,3,4個數字,其余同理),由于 1 1 1是最小的,在如下選擇中只貢獻了 1 1 1次,就是選擇方式為 [ 1 ] [1] [1]的時候。所以我們可以將 m a x v maxv maxv數組排序并用貢獻法求出答案
sort(maxv.begin() + 1, maxv.begin() + n + 1);
LL res = 0, pow2 = 1;
for (int i = 1; i <= n; i ++) {res = (res % mod + maxv[i] * pow2 % mod) % mod;pow2 = pow2 * 2 % mod;
}cout << res << endl;
題目2:Problem - D - Codeforces
大意:給定一個數組 a a a,其長度為 n n n,其中 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1≤n≤106, 1 ≤ a i ≤ n 1 \leq a_i \leq n 1≤ai?≤n。若不存在 k , ( 1 ≤ k ≤ n ) k,(1 \leq k \leq n) k,(1≤k≤n)使得 a k ∣ a i a_k \ | \ a_i ak??∣?ai?, a k ∣ a j a_k \ | \ a_j ak??∣?aj?,那么稱 ( i , j ) (i, j) (i,j)是對兒好整數。求好整數的數量。
-
條件 a k ∣ a i a_k \ | \ a_i ak??∣?ai?, a k ∣ a i a_k \ | \ a_i ak??∣?ai?可以轉化為 a k ∣ g c d ( a i , a j ) a_k \ | \ gcd(a_i, a_j) ak??∣?gcd(ai?,aj?)。由于值域僅有 1 0 6 10^6 106級別,我們可以預處理出 f ( x ) ( 1 ≤ x ≤ 1 0 6 ) f(x) \ (1 \leq x \leq 10^6) f(x)?(1≤x≤106), f ( x ) f(x) f(x)表示 g c d ( i , j ) = x gcd(i, j) = x gcd(i,j)=x好整數的個數。
- 令 y ∣ g c d ( a i , a j ) y\ | \ gcd(a_i, a_j) y?∣?gcd(ai?,aj?),此時 a i a_i ai?, a j a_j aj?是 y y y的倍數,故可以枚舉 y y y的倍數計算出數量 s u m sum sum。那么 y ∣ g c d ( a i , a j ) y \ | \ gcd(a_i,a_j) y?∣?gcd(ai?,aj?)中 ( i , j ) (i, j) (i,j)的對數是 C s u m 2 = s u m × ( s u m ? 1 ) 2 C_{sum}^2 = \frac{sum \times (sum - 1)}{2} Csum2?=2sum×(sum?1)?。其中存在兩種情況 y = g c d ( a i , a j ) y = gcd(a_i, a_j) y=gcd(ai?,aj?)或 y < g c d ( a i , a j ) y < gcd(a_i, a_j) y<gcd(ai?,aj?),(也就是 g c d ( a i , a j ) gcd(a_i, a_j) gcd(ai?,aj?)是 y y y的大于 1 1 1倍的倍數)。所以 f ( x ) = s u m × ( s u m ? 1 ) 2 ? f ( 2 x ) ? f ( 3 x ) ? . . . f(x) = \frac{sum \times (sum - 1)}{2} - f(2x) - f(3x) -... f(x)=2sum×(sum?1)??f(2x)?f(3x)?...
for (int i = n; i >= 1; i --) {int s = 0, t = 0;for (int j = i; j <= n; j += i) {s += cnt[j];t += f[j];}f[i] = s * (s - 1) / 2 - t;}
-
在值域之中,我們將 a m ( 1 ≤ m ≤ n ) a_m(1 \leq m \leq n) am?(1≤m≤n)及其倍數篩掉,剩下的就是 f ( x ) f(x) f(x)中 x x x可以取的值。于是 a n s = ∑ i = 1 n f ( i ) ( 其中 i 未被篩掉 ) ans = \sum^{n}_{i = 1}f(i) \ (其中i未被篩掉) ans=∑i=1n?f(i)?(其中i未被篩掉)
#include <bits/stdc++.h>
using namespace std;using LL = long long;#define int LLvoid solve() {int n;cin >> n;vector<int> f(n + 1, 0);vector<int> cnt(n + 1, 0);for (int i = 0; i < n; i ++) {int x;cin >> x;cnt[x] ++;}for (int i = n; i >= 1; i --) {int s = 0, t = 0;for (int j = i; j <= n; j += i) {s += cnt[j];t += f[j];}f[i] = s * (s - 1) / 2 - t;}vector<int> vis(n + 1, false);for (int i = 1; i <= n; i ++) {if (cnt[i]) {for (int j = i; j <= n; j += i) {vis[j] = true;}}}int res = 0;for (int i = 1; i <= n; i ++) {if (vis[i] == false) {res += f[i];}}cout << res << endl;
}signed main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t --) {solve();}return 0;
}
約數
約數個數
對于一個正整數 x x x,由于唯一分解定理, x = p 1 a 1 × p 2 a 2 × p 3 a 3 × p 4 a 4 . . . x = p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3} \times p_4^{a_4}... x=p1a1??×p2a2??×p3a3??×p4a4??...
對于 x x x的每一個約數 y y y, y y y都能寫成 y = p 1 b 1 × p 2 b 2 × p 3 b 3 × p 4 b 4 . . . ( 0 ≤ b 1 ≤ a 1 , 0 ≤ b 2 ≤ a 2 , 0 ≤ b 3 ≤ a 3 , 0 ≤ b 4 ≤ a 4 ) y = p_1^{b_1} \times p_2^{b_2} \times p_3^{b_3} \times p_4^{b_4}... \ (0 \leq b_1 \leq a_1,0 \leq b_2 \leq a_2, 0 \leq b_3\leq a_3,0 \leq b_4 \leq a_4) y=p1b1??×p2b2??×p3b3??×p4b4??...?(0≤b1?≤a1?,0≤b2?≤a2?,0≤b3?≤a3?,0≤b4?≤a4?)
所以約數個數為 ( a 1 + 1 ) × ( a 2 + 1 ) × ( a 3 + 1 ) × ( a 4 + 1 ) × . . . (a_1 + 1) \times (a_2 + 1) \times (a_3 + 1) \times (a_4 + 1) \times ... (a1?+1)×(a2?+1)×(a3?+1)×(a4?+1)×...
約數之和
約數之和 s u m = ( p 1 0 + p 1 1 + . . . + p 1 a 1 ) × ( p 2 0 + p 2 1 + . . . + p 2 a 2 ) × ( p 3 0 + p 3 1 + . . . + p 3 a 3 ) × ( p 4 0 + p 4 1 + . . . + p 4 a 4 ) × . . . sum = (p_1^0 + p_1^1 + ... + p_1^{a_1}) \times (p_2^0 + p_2^1 + ... + p_2^{a_2}) \times (p_3^0 + p_3^1 + ... + p_3 ^ {a_3}) \times (p_4^0 + p_4^1 + ... + p_4^{a_4}) \times ... sum=(p10?+p11?+...+p1a1??)×(p20?+p21?+...+p2a2??)×(p30?+p31?+...+p3a3??)×(p40?+p41?+...+p4a4??)×...
由分配律將上述括號拆開可得: x 1 + x 2 + x 3 + . . . + x_1 + x_2 + x_3 + ... + x1?+x2?+x3?+...+,其中每一項都是 x x x的約數
#include <bits/stdc++.h>
using namespace std;using LL = long long;const int mod = 1e9 + 7;int n;
map<int, int> cnt;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cin >> n;for (int i = 0; i < n; i ++) {int x;cin >> x;for (int j = 2; j <= x / j; j ++) {if (x % j != 0) continue;int t = 0;while (x % j == 0) {t ++;x /= j;}cnt[j] += t;}if (x > 1) cnt[x] ++;} LL res = 1;for (auto& [u, v] : cnt) {LL t = 0;for (int j = 0; j <= v; j ++) {t = (t * u % mod + 1) % mod;}res = res * t % mod;}cout << res << endl;return 0;
}
組合數
C n m = C n ? 1 m ? 1 + C n ? 1 m C^{m}_{n} = C_{n - 1}^{m - 1} + C_{n - 1}^{m} Cnm?=Cn?1m?1?+Cn?1m?
可由此式子遞推計算組合數
void init() {for (int i = 0; i <= N - 1; i ++) {c[i][0] = 1;}for (int i = 1; i <= N - 1; i ++) {for (int j = 1; j <= N - 1; j ++) {c[i][j] = (c[i - 1][j] % mod + c[i - 1][j - 1] % mod) % mod;}}
}
C n m = A n m A m m = n ! ( n ? m ) ! m ! = n ! m ! ( n ? m ) ! C_n^m = \frac{A_n^m}{A_m^m} = \frac{\frac{n!}{(n - m)!}}{m!} = \frac{n!}{m!(n - m)!} Cnm?=Amm?Anm??=m!(n?m)!n!??=m!(n?m)!n!?
可以預處理出來 f a c t [ N ] fact[N] fact[N]與 i n f a c t [ N ] infact[N] infact[N]
故 C n m = f a c t [ N ] × i n f a c t [ m ] × i n f a c t [ n ? m ] C_n^m = fact[N] \times infact[m] \times infact[n - m] Cnm?=fact[N]×infact[m]×infact[n?m]
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define endl "\n"
typedef pair<int, int> pII;
const int p = 1e9 + 7;
const int N = 100010;
LL fact[N], infact[N];LL qmi(LL a, LL b, LL p) {LL res = 1;while (b) {if (b & 1) res = res * a % p;a = a * a % p;b >>= 1;}return res;
}LL inv(LL x) {return qmi(x, p - 2, p);}void init(int n) {fact[0] = 1;for (int i = 1; i <= n; i ++) fact[i] = fact[i - 1] * i % p;infact[n] = inv(fact[n]);for (int i = n - 1; i >= 0; i --) infact[i] = infact[i + 1] * (i + 1) % p;
}void solve() {int n, m; cin >> n >> m;cout << (fact[n] % p) * (infact[n - m] % p * infact[m] % p) % p << endl;
}int main() {ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);init(100005);int t; cin >> t;while (t --) {solve();}return 0;
}
盧卡斯定理
C n m = C n p m p × C n m o d p m m o d p C_n^m = C^{\frac{m}{p}}_{\frac{n}{p}} \times C^{m \ mod \ p}_{n \ mod \ p} Cnm?=Cpn?pm??×Cn?mod?pm?mod?p?