这部分内容介绍了同余,莫比乌斯函数性质,线性筛,计数初步  
其中计数问题只涉及一些普通的模型
 
计数问题模型初步 
隔板模型 
组合数公式 
( 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(); }