1、欧拉函数
-
计算单个值的欧拉函数
- 基于公式:phi(n) = n\* \\frac{p_1-1}{p_1} \* \\frac{p_2-1}{p_2}\*\\dots \*\\frac{p_n-1}{p_n} 其中 其中 其中p_i 为 为 为n的质因数。
- 写代码用试除法可以快速求解 O ( s q r t ( n ) ) O(sqrt(n)) O(sqrt(n))
-
筛法求欧拉函数(线性筛)
- 代码如下:
Pythonfor i in range(2,n+1): if st[i]==0: primes.append(i) phi[i]=i-1 # 质数的phi(i)=i-1 for j in range(len(primes)): if primes[j]*i>n:break st[primes[j]*i]=1 if i%primes[j]==0: phi[i*primes[j]] = phi[i]*primes[j] # primes[j]是i的质因素数 break phi[i*primes[j]] = phi[i]*(primes[j]-1) # primes[j]不是i的质因数
-
欧拉降幂
- 基本公式
a b ≡ { a b m o d φ ( m ) , g c d ( a , m ) = 1 , a b , g c d ( a , m ) ≠ 1 , b < φ ( m ) , ( m o d m ) a ( b m o d φ ( m ) ) + φ ( m ) , g c d ( a , m ) ≠ 1 , b ≥ φ ( m ) . a^b \equiv \begin{cases} a^{b \ mod \ \varphi(m)}, \ \ \ \ \ \ \ \ \ \ \ \ \ gcd(a,m)=1, \\ a^b, \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ gcd(a,m) \not=1,b<\varphi(m),\ \ \ \ (mod \ \ m)\\ a^{(b \ mod \ \varphi(m)) + \varphi(m)}, \ \ gcd(a,m) \not =1,b\ge \varphi(m). \end{cases} ab≡⎩ ⎨ ⎧ab mod φ(m), gcd(a,m)=1,ab, gcd(a,m)=1,b<φ(m), (mod m)a(b mod φ(m))+φ(m), gcd(a,m)=1,b≥φ(m).
- 基本公式
2、乘法逆元
-
费马小定理求逆元
- p o w ( a , p , p − 2 ) pow(a,p,p-2) pow(a,p,p−2)
-
扩展欧几里得算法求逆元
- e x g c d ( a , p , x , y ) exgcd(a,p,x,y) exgcd(a,p,x,y)
- 如果求得最大公约数不是1则无解,否则逆元就是 x x%p x
pythondef exgcd(a,b,x,y): if b==0: return a,1,0 d,x,y = exgcd(b,a%b,x,y) x,y = y,x-a//b*y return d,x,y
3、中国剩余定理
-
C R T CRT CRT中国剩余定理基本形式
- 中国剩余定理给出了以下的一元线性同余方程组的解:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋮ x ≡ a n ( m o d m n ) \begin{cases} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \qquad \vdots \\ x \equiv a_n \pmod{m_n} \end{cases} ⎩ ⎨ ⎧x≡a1(modm1)x≡a2(modm2)⋮x≡an(modmn)
- 在中国剩余定理中: a 1 , a 2 , ... , a n a_1, a_2, \ldots, a_n a1,a2,...,an 是给定的余数, m 1 , m 2 , ... , m n m_1,m_2,\ldots,m_n m1,m2,...,mn两两互质,如果两两不互质则是扩展中国剩余定理。
-
C R T CRT CRT问题的解决方法
-
CRT主要利用 m 1 , m 2 , ... , m n m_1,m_2,\dots,m_n m1,m2,...,mn 为两两互质的整数的性质。我们可以令:
M = m 1 × m 2 × ⋯ × m n = ∏ i = 1 n m i M i = M / m i M=m_1 \times m_2 \times \dots \times m_n = \begin{equation} \prod_{i=1}^{n}m_i \end{equation}\\ M_i = M/m_i M=m1×m2×⋯×mn=i=1∏nmiMi=M/mi明显这里的 M i M_i Mi是和 m i m_i mi互质的。
-
接着我们定义逆元:
t i = M i − 1 t_i = M_i^{-1} ti=Mi−1 -
所以我们可以构造出一个解 x 0 x_0 x0
x 0 = ∑ i = 1 n a i M i t i x_0 = \sum_{i=1}^{n}a_iM_it_i x0=i=1∑naiMiti -
解释 ∀ j ∈ [ 1 , n ] \forall j \in [1,n] ∀j∈[1,n]:
{ a j M j t j % m i = 0 j ≠ i a j M j t j % m i = a i j = i \begin{cases} a_jM_jt_j \% m_i = 0 \ \ \ \ \ \ j\neq i \\ a_jM_jt_j \% m_i = a_i \ \ \ \ \ j=i \end{cases} {ajMjtj%mi=0 j=iajMjtj%mi=ai j=i -
通解就是 x = x 0 + k M x = x_0 + kM x=x0+kM,而CRT问题所求的最小整数解其实就是 a n s = x % M ans = x \% M ans=x%M
-
-
E X C R T EXCRT EXCRT扩展中国剩余定理的基本用法性质
-
同余方程合并后模数是原来模数的最小公倍数。
-
合并的流程:
-
假设前 k − 1 k-1 k−1个同余方程合并得到新的方程为: x = r 1 ( m o d M ) x = r_1 \pmod{M} x=r1(modM) , M M M是前 k − 1 k-1 k−1个同余方程模数的最小公倍数,现在需要考虑合并第 k k k个方程: x = r 2 ( m o d m k ) x = r_2 \pmod{m_k} x=r2(modmk)。
-
对于前 k − 1 k-1 k−1个同余方程,其通解为 x = r 1 + i ∗ M , i ∈ Z x=r_1+i*M,i \in Z x=r1+i∗M,i∈Z,接着在通解里面找到一个 t t t,使得 ( r 1 + t ∗ M ) % m k = r 2 (r_1+t*M) \%m_k=r_2 (r1+t∗M)%mk=r2,即可满足第 k k k个方程。那么合并后前 k k k个同余方程的通解就是
x = r 1 + t ∗ M + i ∗ l c m ( M , m k ) , i ∈ Z x = r_1 + t*M + i* lcm(M,m_k), \ i \in Z x=r1+t∗M+i∗lcm(M,mk), i∈Z再对通解模 l c m ( M , m k ) lcm(M,m_k) lcm(M,mk) 即可得到新的 a n s ans ans作为前 k k k个同余方程的最小整数解。
-
其中找 t t t的过程: M ∗ t + m k ∗ y = r 2 − r 1 M*t + m_k*y=r_2-r_1 M∗t+mk∗y=r2−r1,其中 M M M就是裴蜀等式的 a , t a,t a,t就是裴蜀等式的 x x x,其中 r 2 − r 1 r_2-r_1 r2−r1要满足 g c d ( M , m k ) ∣ ( r 2 − r 1 ) gcd(M,m_k)|(r_2-r_1) gcd(M,mk)∣(r2−r1),否则无解。
-
我们先用扩展欧几里得求解出 M ∗ t + m k ∗ y = g c d ( M , m k ) M*t + m_k*y=gcd(M,m_k) M∗t+mk∗y=gcd(M,mk),顺便求出 g c d ( M , m k ) gcd(M,m_k) gcd(M,mk),再将得到的解 t = t ∗ r 2 − r 1 g c d ( M , m k ) t = t* \frac{r_2-r_1}{gcd(M,m_k)} t=t∗gcd(M,mk)r2−r1,即可求得 t t t。
-
-
4、组合数
-
方法一:利用递推公式: c [ a ] [ b ] = c [ a − 1 ] [ b ] + c [ a − 1 ] [ b − 1 ] c[a][b] = c[a-1][b]+c[a-1][b-1] c[a][b]=c[a−1][b]+c[a−1][b−1]即考虑当前选不选的问题。 O ( n 2 ) O(n^2) O(n2)其中( n ≤ 2000 n \le 2000 n≤2000)
-
方法二:预处理逆元法:根据公式计算: C a b = a ! ( a − b ) ! b ! C_a^{b} = \frac{a!}{(a-b)!b!} Cab=(a−b)!b!a! 由于后面数值一般很大会设置一个模数,可以考虑计算出 f a c [ i ] fac[i] fac[i]表示 i i i的阶乘, i n f a c [ i ] infac[i] infac[i]表示 i i i的阶乘的逆元。因此: C a b = f a c [ a ] ∗ i n f a c [ a − b ] % p ∗ i n f a c [ b ] % p C_a^{b}=fac[a]*infac[a-b]\%p*infac[b]\%p Cab=fac[a]∗infac[a−b]%p∗infac[b]%p.此方法可以预处理出$n \le 1e5 $的数。
-
方法三:利用 L u c a s Lucas Lucas定理: C a b = C a % p b % p ∗ C a / / p b / / p ( m o d p ) C_a^{b}=C_{a\%p}^{b\%p}*C_{a//p}^{b//p}\ (mod \ p) Cab=Ca%pb%p∗Ca//pb//p (mod p)可以处理( 1 ≤ b ≤ a ≤ 1 0 18 1 \le b \le a \le10^{18} 1≤b≤a≤1018, p p p为质数)
pythondef lucas(a,b,p): if a<p and b<p:return C(a,b,p) return C(a%p,b%p,p)*lucas(a//p,b//p,p)%p
- 应用:括号序列( C 2 n n − C 2 n n − 1 C_{2n}^{n}-C_{2n}^{n-1} C2nn−C2nn−1),这个也称为卡特兰数(合法出栈数)
- 卡特兰数公式: f ( n ) = C 2 n n n + 1 = C 2 n n − C 2 n n − 1 f(n)=\frac{C_{2n}^{n}}{n+1}=C_{2n}^{n}-C_{2n}^{n-1} f(n)=n+1C2nn=C2nn−C2nn−1
- 应用:括号序列( C 2 n n − C 2 n n − 1 C_{2n}^{n}-C_{2n}^{n-1} C2nn−C2nn−1),这个也称为卡特兰数(合法出栈数)
5、容斥原理
-
二项式反演:
-
恰好和至少的转换(常用到)
- 设 f i f_i fi表示至少有 k k k个的方案数, g i g_i gi表示恰好有 k k k个方案数,则有:
f k = ∑ i = k n ( n k ) g i f_k=\sum_{i=k}^{n} \binom{n}{k}g_i fk=i=k∑n(kn)gi
- 根据二项式反演则有:
g k = ∑ i = k n ( − 1 ) i − k ( i k ) f i g_k=\sum_{i=k}^{n}(-1)^{i-k} \binom{i}{k}f_i gk=i=k∑n(−1)i−k(ki)fi
- 当我们需要求恰好 k k k个方案,同时发现至少 k k k个方案比较容易求时可以建议用这个。
-
恰好和至多的转换(没上面常用)
- 设 f i f_i fi表示至多有 k k k个方案数, g i g_i gi表示恰好有 k k k个方案数,则有:
f k = ∑ i = 0 n ( n k ) g i f_k=\sum_{i=0}^{n} \binom{n}{k}g_i fk=i=0∑n(kn)gi
- 根据二项式反演则有:
g k = ∑ i = 0 n ( − 1 ) k − i ( i k ) f i g_k=\sum_{i=0}^{n} (-1)^{k-i} \binom{i}{k}f_i gk=i=0∑n(−1)k−i(ki)fi
- 当发现最多有 k k k个的时候好算时用这个。
-
6、博弈论
-
四种经典组合游戏
- 尼姆游戏: n n n堆物品,每堆 a i a_i ai个,两名玩家轮流,每次选一堆,至少取1个最多可全部取走。
- 结论:令S=\\oplus_{i=1}\^n a_i 先手必胜当且仅当 先手必胜当且仅当 先手必胜当且仅当S\\not =0.(根据异或性质容易判断)
- 巴什博弈: 1 1 1堆石子,总数 n n n个,两名玩家轮流取,每次至少取1个,最多取m个,取走最后一个的玩家获胜。
- 结论:可以发现不管怎么取都可以操作出 m + 1 m+1 m+1个石子,所以当 ( m + 1 ) ∣ n (m+1)|n (m+1)∣n时先手必败。
- 威佐夫博弈: 2 2 2堆石子,石子数可以不同,两名玩家轮流取石子,每次可以从一堆中取或从两堆中取走相同个石子。取走最后一个石子的人获胜。
- 结论:设 x = 1 + 5 2 x=\frac{1+\sqrt 5}{2} x=21+5 ,则第 i i i个必败态( a i , b i a_i,b_i ai,bi)满足a_i=\\lfloor ix\\rfloor , , ,b_i=i+a_i=\\lfloor ix\^2 \\rfloor.
- 在实际代码中就是 m i n ( a , b ) = i n t ( a b s ( a − b ) ∗ 1 + 5 2 ) min(a,b)= int(abs(a-b)*\frac{1+\sqrt 5}{2}) min(a,b)=int(abs(a−b)∗21+5 )则先手必败,反之 w i n win win.
- 斐波那契博弈: 1 1 1堆石子,总数 n n n个,两名玩家轮流取,先手不能一次取完,至少1个,之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。取走最后一个的获胜。
- 结论:当且仅当石子个数为斐波那契数时先手必败。
- 尼姆游戏: n n n堆物品,每堆 a i a_i ai个,两名玩家轮流,每次选一堆,至少取1个最多可全部取走。
-
N i m − S G Nim-SG Nim−SG函数
-
尼姆游戏的变种总结出来的规律。 M e x Mex Mex运算: m e x ( S ) = min { x ∈ N ∣ x ∉ S } { x } mex(S)=\min\limits_{\{x \in \mathbb{N} \mid x \not \in S \}} \{x\} mex(S)={x∈N∣x∈S}min{x}也就是不在 S S S集合中的最小自然数
-
S G SG SG函数计算每一个节点的 s g sg sg值, S G ( x ) = m e x ( { S G ( y 1 ) , S G ( y 2 ) , ... , S G ( y k ) } ) SG(x)=mex(\{SG(y_1),SG(y_2),\dots,SG(y_k)\}) SG(x)=mex({SG(y1),SG(y2),...,SG(yk)}),其中 y 1 , y 2 , ... , y k y_1,y_2,\dots,y_k y1,y2,...,yk是 x x x的后继节点。
pythondef mex(lst): lst = list(set(lst)) return next((idx for idx,num in enumerate(lst) if idx!=num),lst[-1]+1) def SG(lst,n): sg = [0]*(n+5) for i in range(1,n+1): sg[i] = mex([sg[i-j] for j in lst if i-j>=0]) return sg
-
这个模板基本通用了。
-