这部分内容介绍了同余,莫比乌斯函数性质,线性筛,计数初步
其中计数问题只涉及一些普通的模型
计数问题模型初步
隔板模型
组合数公式
( n m ) = ( n n − m ) {n \choose m} = {n \choose n-m}
( m n ) = ( n − m n )
( n m ) = ( n − 1 m ) + ( n − 1 m − 1 ) {n \choose m} = {n-1 \choose m} + {n-1 \choose m-1}
( m n ) = ( m n − 1 ) + ( m − 1 n − 1 )
proof
对于第 n n n 个元素,要么取,要么不取
对应的在剩下的 n − 1 n-1 n − 1 个元素中取 m − 1 m-1 m − 1 个元素,或者是 m m m 个元素
( n 0 ) + ( n 1 ) + ( n 2 ) + ⋯ + ( n n ) = 2 n {n \choose 0} + {n \choose 1} + {n \choose 2} + \cdots + {n \choose n} = 2^n
( 0 n ) + ( 1 n ) + ( 2 n ) + ⋯ + ( n n ) = 2 n
proof
每一个元素都有2种可能,取或者不取,一共是 2 n 2^n 2 n 种可能
另外,最后的方案数可能有 0 , 1 , 2 , ⋯ , n 0, 1, 2, \cdots, n 0 , 1 , 2 , ⋯ , n 个元素
根据加法原理,一共有 ( n 0 ) + ( n 1 ) + ( n 2 ) + ⋯ + ( n n ) {n \choose 0} + {n \choose 1} + {n \choose 2} + \cdots + {n \choose n} ( 0 n ) + ( 1 n ) + ( 2 n ) + ⋯ + ( n n ) 种不同的方案
UVA10943
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const int maxn = 200 + 10; const int mod = 1e6; int c[maxn][maxn]; int n, k; void prework () { memset(c, 0, sizeof(c)); _for(i, 1, maxn) { c[i][0] = c[i][i] = 1; _for(j, 1, i) { c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod; } } } int main () { freopen("input.txt" , "r" , stdin); prework(); while (scanf("%d%d" , &n, &k) == 2 && n) { // init printf ("%d\n" , c[n+k-1][k-1]); } }
重复元素的全排列
UVA11076
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 const int maxn = 15; ll fac[maxn], num[maxn]; const ll one[13] = {0,1,11,111,1111,11111,111111,1111111,11111111,111111111,1111111111,11111111111,111111111111}; int n; void init () { // memset(num, 0, sizeof(num)); } void prework () { memset(fac, 0, sizeof(fac)); fac[0] = 1, fac[1] = 1; for (int i = 2; i <= 12; i++) fac[i] = fac[i-1] * 1ll * i; } int main () { /* string str; for (int i = 1; i <= 12; i++) { str.clear(); for (int t = 0; t < i; t++) str += '1' ; cout << str << ','; } */ freopen("input.txt", "r", stdin); prework(); while (scanf("%d", &n) == 1 && n) { init(); ll sum = 0; _for(i, 0, n) { int x; scanf("%d", &x); sum += x; num[x]++; } ll ans = sum * fac[n-1]; _rep(i, 0, 9) ans /= fac[num[i]]; ans *= one[n]; printf("%lld\n", ans); } }
同余问题
同余类与最小剩余系
Euler-Fermat定理求解一次同余式
( a , m ) = 1 (a,m)=1 ( a , m ) = 1 ,那么一次同余式
a x ≡ b ( m o d m ) ax \equiv b (\bmod m)
a x ≡ b ( m o d m )
的唯一解由
x ≡ b a ϕ ( m ) − 1 ( m o d m ) x \equiv ba^{\phi(m)-1} (\bmod m)
x ≡ b a ϕ ( m ) − 1 ( m o d m )
给出
HDU2462
x x x 个 8 8 8 连在一起组成的正整数可以写成
8 × 1 0 x − 1 9 8 \times \frac{10^x - 1}{9}
8 × 9 1 0 x − 1
求最小的 x x x ,满足 9 L ∣ 8 ( 1 0 x − 1 ) 9L \mid 8(10^x - 1) 9 L ∣ 8 ( 1 0 x − 1 )
let d = gcd ( L , 8 ) \textbf{let} \ d = \textbf{gcd}(L, 8) let d = gcd ( L , 8 )
⟹ 9 L d ∣ ( 1 0 x − 1 ) ⇒ 1 0 x ≡ 1 ( m o d 9 L d ) \Longrightarrow \frac{9L}{d} \mid (10^x -1) \Rightarrow 10^x \equiv 1 (\bmod \frac{9L}{d})
⟹ d 9 L ∣ ( 1 0 x − 1 ) ⇒ 1 0 x ≡ 1 ( m o d d 9 L )
引理 ,若 ( a , n ) = 1 (a, n)=1 ( a , n ) = 1 ,则满足 a x ≡ 1 ( m o d n ) a^x \equiv 1 (\bmod n) a x ≡ 1 ( m o d n ) 的最小正整数 x 0 x_0 x 0 ,满足
x 0 ∣ ϕ ( n ) x_0 \mid \phi(n) x 0 ∣ ϕ ( n )
proof
反证法,不妨设 ϕ ( n ) = q x 0 + r ( 0 < r < x 0 ) \phi(n) = qx_0 + r \quad (0 < r < x_0) ϕ ( n ) = q x 0 + r ( 0 < r < x 0 )
a x 0 ≡ 1 ( m o d n ) ⇒ a q x 0 ≡ 1 ( m o d n ) a^{x_0} \equiv 1 (\bmod n) \Rightarrow a^{qx_0} \equiv 1(\bmod n) a x 0 ≡ 1 ( m o d n ) ⇒ a q x 0 ≡ 1 ( m o d n )
根据欧拉定理 ,有 a ϕ ( n ) ≡ 1 ( m o d n ) a^{\phi(n)} \equiv 1(\bmod n) a ϕ ( n ) ≡ 1 ( m o d n )
{ a q x 0 ≡ 1 ( m o d n ) a ϕ ( n ) ≡ 1 ( m o d n ) ⇒ a r ≡ 1 ( m o d n ) ⇒ r < x 0 \begin{cases}
a^{qx_0} \equiv 1 (\bmod n) \\
a^{\phi(n)} \equiv 1 (\bmod n)
\end{cases}
\Rightarrow a^{r} \equiv 1(\bmod n) \Rightarrow r < x_0
{ a q x 0 ≡ 1 ( m o d n ) a ϕ ( n ) ≡ 1 ( m o d n ) ⇒ a r ≡ 1 ( m o d n ) ⇒ r < x 0
矛盾
先重温一下快速幂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 ll L; const ll inf = 1e18; ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); } ll ksc(ll a, ll b, ll mod) { ll ans = 0; for (; b; b >>= 1) { if (b & 1) ans = (ans + a) % mod; a = (a * 2) % mod; } return ans; } ll ksm(ll a, ll b, ll mod) { ll ans = 1 % mod; a %= mod; for (; b; b >>= 1) { if (b & 1) ans = ksc(ans, a, mod); a = ksc(a, a, mod); } return ans; } ll phi(ll n) { ll ans = n; for (ll i = 2; i * i <= n; i++) { if (n % i == 0) { ans = ans / i * (i - 1); while (n % i == 0) n /= i; } } if (n > 1) ans = ans / n * (n - 1); return ans; } ll solve () { ll d = gcd(L, 8ll); ll mod = 9 * L / d; if (gcd(mod, 10ll) != 1) return 0; ll N = phi(mod); ll res = inf; for (ll i = 1; i <= sqrt(N); i++) { if (N % i) continue ; if (ksm(10ll, i, mod) == 1) chmin(res, i); if (ksm(10ll, N/i, mod) == 1) chmin(res, N/i); } return res == inf ? 0 : res; } int main () { freopen("input.txt" , "r" , stdin); int kase = 0; while (cin >> L && L) { printf ("Case %d: " , ++kase); ll res = solve(); printf ("%lld\n" , res); } }
扩展Euclid算法
Bezout定理
对于任意整数 a , b a, b a , b ,存在一对整数 x , y x,y x , y ,满足 a x + b y = gcd ( a , b ) ax+by = \textbf{gcd}(a, b) a x + b y = gcd ( a , b )
根据数学归纳法做递归
i) b = 0 \textbf{i)} \ b = 0 i) b = 0 ,此时存在一对解 x = 1 , y = 0 x = 1, y = 0 x = 1 , y = 0
a ⋅ 1 + 0 ⋅ 0 = gcd ( a , 0 ) = a \quad a\cdot 1 + 0 \cdot 0=\textbf{gcd}(a,0)=a a ⋅ 1 + 0 ⋅ 0 = gcd ( a , 0 ) = a
ii) b > 0 \textbf{ii)} \ b > 0 ii) b > 0
{ a x + b y = gcd ( a , b ) gcd ( a , b ) = gcd ( b , a m o d b ) b x + ( a m o d b ) y = gcd ( b , a m o d b ) \begin{cases}
ax+by = \textbf{gcd}(a, b) && \textbf{gcd}(a, b) = \textbf{gcd}(b, a \bmod b) \\
bx + (a \bmod b) y = \textbf{gcd}(b, a \bmod b)
\end{cases}
{ a x + b y = gcd ( a , b ) b x + ( a m o d b ) y = gcd ( b , a m o d b ) gcd ( a , b ) = gcd ( b , a m o d b )
b x + ( a m o d b ) y = b x + ( a − ⌊ a / b ⌋ b ) y = a y + b ( x − ⌊ a / b ⌋ y ) bx+(a \bmod b)y = bx+(a- \lfloor a/b \rfloor b) y = ay + b(x - \lfloor a/b\rfloor y)
b x + ( a m o d b ) y = b x + ( a − ⌊ a / b ⌋ b ) y = a y + b ( x − ⌊ a / b ⌋ y )
x ′ ← y x' \leftarrow y x ′ ← y
y ′ ← x − ⌊ a / b ⌋ y y' \leftarrow x - \lfloor a/b\rfloor y y ′ ← x − ⌊ a / b ⌋ y
a x ′ + b y ′ = gcd ( a , b ) ax'+by' = \textbf{gcd}(a, b) a x ′ + b y ′ = gcd ( a , b )
1 2 3 4 5 6 7 8 9 10 int exgcd(int a, int b, int& x, int& y) { if (b == 0) { x = 1, y = 0; return a; } int d = exgcd(b, a % b, x, y); int z = x; x = y; y = z - (a/b) * y; return d; }
对于一般方程 a x + b y = c ax+by = c a x + b y = c ,可以先求出最大公约数 ( a , b ) = d (a, b) = d ( a , b ) = d
有解当且仅当 d ∣ c d \mid c d ∣ c
调用
求出方程的一组特解 x 0 , y 0 x_0, y_0 x 0 , y 0 ,原方程的一组特解就是
c d ⋅ x 0 , c d ⋅ y 0 \frac{c}{d} \cdot x_0, \quad \frac{c}{d} \cdot y_0
d c ⋅ x 0 , d c ⋅ y 0
通解是
x = c d x 0 + k b d , y = c d y 0 − k a d ( k ∈ Z ) x = \frac{c}{d} x_0 + k \frac{b}{d}, \quad y = \frac{c}{d} y_0 - k \frac{a}{d} \quad (k \in \mathbb{Z})
x = d c x 0 + k d b , y = d c y 0 − k d a ( k ∈ Z )
乘法逆元
a ⋅ 1 b ≡ a ⋅ x ( m o d m ) a \cdot \frac{1}{b} \equiv a \cdot x (\bmod m)
a ⋅ b 1 ≡ a ⋅ x ( m o d m )
其中 b ∣ a , gcd ( b , m ) = 1 b \mid a, \ \textbf{gcd}(b,m) = 1 b ∣ a , gcd ( b , m ) = 1
x x x 是 b b b 的 m o d m \bmod m m o d m 乘法逆元
记为 b − 1 ( m o d m ) b^{-1} (\bmod m) b − 1 ( m o d m )
a / b ≡ a ⋅ b − 1 ≡ a / b ⋅ b ⋅ b − 1 ( m o d m ) a/b \equiv a \cdot b^{-1} \equiv a/b \cdot b \cdot b^{-1} (\bmod m) a / b ≡ a ⋅ b − 1 ≡ a / b ⋅ b ⋅ b − 1 ( m o d m )
⇒ b ⋅ b − 1 ≡ 1 ( m o d m ) \Rightarrow b \cdot b^{-1} \equiv 1 (\bmod m) ⇒ b ⋅ b − 1 ≡ 1 ( m o d m )
如果 m m m 是素数,并且 b < p b < p b < p ,有 b p − 1 ≡ 1 ( m o d p ) ⇔ b ⋅ b p − 2 ≡ 1 ( m o d m ) b^{p-1} \equiv 1 (\bmod p) \Leftrightarrow b \cdot b^{p-2} \equiv 1(\bmod m) b p − 1 ≡ 1 ( m o d p ) ⇔ b ⋅ b p − 2 ≡ 1 ( m o d m )
p p p 为质数的时候, b p − 2 b^{p-2} b p − 2 即为 b b b 的乘法逆元
如果 ( b , m ) = 1 (b,m)=1 ( b , m ) = 1 ,乘法逆元可以通过求解线性同余方程得到
b ⋅ x ≡ 1 ( m o d m ) b \cdot x \equiv 1 (\bmod m) b ⋅ x ≡ 1 ( m o d m )
乘法逆元在计算中的应用
a b ⟹ a ← a m o d p , b ← b m o d p \frac{a}{b} \Longrightarrow {a \leftarrow a \bmod p}, \ {b \leftarrow b \bmod p}
b a ⟹ a ← a m o d p , b ← b m o d p
a ⋅ b − 1 m o d p a \cdot b^{-1} \bmod p a ⋅ b − 1 m o d p 作为最终结果
当然前提是必须保证 ( b , p ) = 1 (b,p)=1 ( b , p ) = 1 ,也就是说 b b b 不是 p p p 的倍数
POJ1845
S i = p i B ⋅ c i + 1 − 1 p i − 1 S_i = \frac{p_i^{B \cdot c_i + 1} - 1}{p_i - 1}
S i = p i − 1 p i B ⋅ c i + 1 − 1
A B = ∏ i ∈ [ 1 , n ] S i , M = 9901 A^B = \prod_{i \in [1,n]} S_i, M = 9901 A B = ∏ i ∈ [ 1 , n ] S i , M = 9 9 0 1
algorithm \textbf{algorithm} algorithm
( p i B ⋅ c i + 1 − 1 ) m o d M , ( p i − 1 ) m o d M (p_i^{B \cdot c_i + 1} - 1 ) \bmod M, \quad (p_i - 1) \bmod M ( p i B ⋅ c i + 1 − 1 ) m o d M , ( p i − 1 ) m o d M
如果 p i − 1 p_i - 1 p i − 1 不是 M M M 的倍数,那么这个数的乘法逆元
i n v = ( p i − 1 ) M − 2 inv = (p_i - 1)^{M-2} i n v = ( p i − 1 ) M − 2 ,用 × i n v \times inv × i n v 代替除法
如果 p i − 1 = k M p_i - 1 = kM p i − 1 = k M ,那么 p i ≡ 1 ( m o d M ) p_i \equiv 1 (\bmod M) p i ≡ 1 ( m o d M )
1 + p i + p i 2 + ⋯ + p i B ⋅ c i ≡ 1 + 1 + ⋯ ≡ B ⋅ c i + 1 ( m o d M ) 1 + p_i + p_i^{2} + \cdots + p_i^{B\cdot c_i} \equiv 1 + 1+ \cdots \equiv B \cdot c_i + 1(\bmod M) 1 + p i + p i 2 + ⋯ + p i B ⋅ c i ≡ 1 + 1 + ⋯ ≡ B ⋅ c i + 1 ( m o d M )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 const int mod = 9901; ll a, b; class A { public: int p, c; A() = default; A(int p) : p(p) { c = 0; } }; vector<A> P; void divide(ll n) { P.clear(); for (ll i = 2; i * i <= n; i++) { if (n % i == 0) { P.push_back(A(i)); while (n % i == 0) n /= i, P.back().c++; } } if (n > 1) P.push_back(n), P.back().c = 1; } void solve () { ll ans = 1; divide(a); _for(i, 0, P.size()) { if ((P[i].p - 1) % mod == 0) { ans = (b * P[i].c + 1) % mod * ans % mod; continue ; } ll x = ksm(P[i].p, b*P[i].c+1, mod); x = (x - 1 + mod) % mod; ll y = P[i].p - 1; y = ksm(y, mod - 2, mod); ans = ans * x % mod * y % mod; } cout << ans << endl; } void init() { // } int main() { freopen("input.txt", "r", stdin); cin >> a >> b; init(); // then solve solve(); }