Day1 20.02.11
一、素数判定:Miller-Rabbin
①费马小定理:$a^{p-1} \equiv 1 \pmod p$($p$为素数).
因此可以选取若干个 $a$,对 $p$ 进行判定. 满足条件的 $p$ 为素数的概率在 $\frac{3}{4}$ 左右.
②二次探测定理:$x^2 \equiv 1 \pmod p$ 当 $p$ 为大于 $2$ 的素数时仅有两个解 $x_1=1,x_2=p-1$.
因此考虑选取 $9$ 至 $12$ 个素数,先使用费马小定理判定,再对 $a^{\frac{p-1}{2^k}}$ 使用二次探测判定.
代码如下:

inline long long mul ( long long x,long long y,long long mod ) { return (long long)((__int128)x*y%mod); } inline long long power ( long long x,long long y,long long mod ) { long long z=1; for ( ;y;y>>=1,x=mul(x,x,mod) ) if ( y&1 ) z=mul(z,x,mod); return z; } const long long Prime[13]={1,2,3,5,7,11,13,17,19,61,2333,4567,24251}; long long Pow[100],ans; inline bool Miller_Rabbin ( long long p ) { if ( p==1 ) return false; for ( int i=1;i<=12;i++ ) { if ( p==Prime[i] ) return true; if ( !(p%Prime[i]) or power(Prime[i]%p,p-1,p)!=1 ) return false; long long x=p-1; while ( !(x&1) ) { x>>=1; long long res=power(Prime[i]%p,x,p); if ( res!=1 and res!=p-1 ) return false; if ( res==p-1 ) break; } } return true; }
二、素数筛法:线性筛(欧拉筛)
①埃氏筛:枚举每个素数 $p$,将 $p$ 的倍数标记为非素数. 可以证明效率为 $O(n \log \log n)$.
②欧拉筛:考虑对埃氏筛进行优化. 不难发现,埃氏筛的问题在于对多因子的合数进行了多次标记.
考虑只对每个数标记一次. 令每个数只被其最小的素因子筛到. 考虑转化为枚举除去最小素因子的剩下的因子.
对于每个数枚举小于其的素数. 将这两个数的积标记为非素数. 当枚举的素数是当前数的因子是退出循环即可.
效率 $O(n)$. 代码如下:

bool flag[100000000]; std::vector<int> pr; inline void solve ( int n ) { for ( int i=2;i<=n;i++ ) { if ( !flag[i] ) pr.push_back(i); for ( int j:pr ) { if ( i*j>n ) break; flag[i*j]=true; if ( !(i%j) ) break; } } }
线性筛还可以用来求积性函数. 后文 Day4 部分有提及.
三、质因数分解:Pollard-Rho
前置知识:素数判定 Miller-Rabbin.
引理:生日悖论:$23$ 人中,存在两个人在同一天生日的概率超过 $\frac{1}{2}$.
考虑找到一个数 $a$ 使得 $1 < \gcd(a,n) < n$. 则有:$\gcd(a,n) \mid n, \frac{n}{\gcd(a,n)} \mid n$.
考虑用随机的方式找到 $a$. 构造函数 $f(x)=(x^2+c) \bmod n$. 其中 $c$ 为一个固定的随机数.
先任意取 $x_1$,然后令 $x_i=f(x_{i-1})(i \geq 2)$ 即可.
观察生成的随机数列,发现分布较为均匀.
但注意到一个问题:生成的数列在一定长度后会陷入循环,形成 $\rho$ 形数列.
因此每次判断 $d=\gcd(\lvert x_i-x_1 \rvert,n)$ 是否符合要求. 当 $x_i=x_1$ 时退出循环. 由引理知这样一个环的期望长度为 $O(\sqrt n)$.
考虑对求解的过程进行优化. 由于计算 $\gcd$ 的时间较慢,考虑每隔一段时间统一计算. 采用倍增的思想,统计 $res=(\prod \lvert x_i - x_1 \rvert) \bmod n$.
当 $res=0$ 时出现环. 每隔 $2^k$ 次计算一次 $\gcd$,重新初始化即可. 代码如下:

1 inline long long work ( long long x ) 2 { 3 long long s=0,t=0,c=1LL*rand()*rand()*rand()%(x-1)+1; 4 for ( int step=1;;step<<=1,s=t ) 5 { 6 long long res=1; 7 for ( int i=1;i<=step;i++ ) 8 { 9 t=(mul(t,t,x)+c)%x; 10 res=mul(res,std::abs(t-s),x); 11 if ( !(i%127) ) 12 { 13 long long d=std::__gcd(res,x); 14 if ( d!=1 ) return d; 15 } 16 } 17 long long d=std::__gcd(res,x); 18 if ( d!=1 ) return d; 19 } 20 return -1; 21 } 22 inline void Pollard_Rho ( long long x ) 23 { 24 if ( x==1 or x<=ans ) return; 25 if ( Miller_Rabbin(x) ) { ans=std::max(ans,x);return; } 26 long long y=x; 27 while ( y>=x ) y=work(x); 28 while ( !(x%y) ) x/=y; 29 Pollard_Rho(x);Pollard_Rho(y); 30 }
四、BSGS/exBSGS
1. BSGS
求解方程:$y^x \equiv z \pmod p$($p$ 为素数).
令 $x=am-b(0 \leq b \leq m-1)$,则有:$y^{am} \equiv z \times y^b \pmod p$.
等式右边有 $m$ 种取值,用 hash_table 或 unordered_map 记录.
等式左边枚举 $a$,查询右边是否出现过该值即可.
当 $m=\sqrt p$ 时效率最优,效率为 $O(\sqrt p)$.
实际写代码时由于数据原因,常将 $m$ 与一个常量(例如 $1000$,视数据而定)取 $\min$.
代码如下:

1 std::unordered_map<int,int> map; 2 inline int BSGS ( int y,int z,int p ) 3 { 4 int m=(int)sqrt(p)+1; 5 if ( z%p==1 ) return 0; 6 if ( !(y%p) ) return -1; 7 map.clear(); 8 for ( int i=0,t=z;i<m;i++,t=1LL*t*y%p ) map[t]=i; 9 for ( int i=1,v=power(y,m,p),t=v;i<=m+1;i++,t=1LL*t*v%p ) if ( map.count(t) ) return i*m-map[t]; 10 return -1; 11 }
2. exBSGS
求解方程:$y^x \equiv z \pmod p$($p$ 为任意正整数).
将等式展开有:$y \times y^{x-1} + k \times p = z(k \in \mathbb{Z})$.
由裴属定理(Day2 内容)知,当且仅当 $d=gcd(y,k) \mid z$ 时有解.
若有解则有:$\frac{y}{d} \times y^{x-1} + k \times \frac{p}{d} = z$.
不断递归求解,最终得到 $\gcd(y,p)=1$ 时即可用 BSGS 求解.
设递归 $c$ 次,$g=\prod\limits_{i=1}^{c} d_i$.
则有:$y^{x-c} \times \frac{y^c}{g} \equiv \frac{z}{g} \pmod {\frac{p}{g}}$.
代码如下:

1 std::unordered_map<int,int> map; 2 inline int exBSGS ( int y,int p,int z ) 3 { 4 if ( z==1 ) return 0; 5 int c=0,g=1; 6 for ( int d=std::__gcd(y,p);d!=1;d=std::__gcd(y,p) ) 7 { 8 if ( z%d ) return -1; 9 c++;z/=d;p/=d;g=1LL*g*y/d%p; 10 if ( z==g ) return c; 11 } 12 int m=sqrt(p)+1;map.clear(); 13 for ( int i=0,t=z;i<m;i++,t=1LL*t*y%p ) map[t]=i; 14 for ( int i=1,v=power(y,m,p),t=1LL*g*v%p;i<=m+1;i++,t=1LL*t*v%p ) if ( map.count(t) ) return i*m-map[t]+c; 15 return -1; 16 }
Day2 20.02.12
一、裴蜀定理
裴蜀定理:方程 $\sum\limits_{i=1}^{n} a_i \times x_i = b$ 有整数解的充要条件为 $\gcd(a_1,a_2,\dots,a_n \mid b$.
证明:
①必要性:
令 $d=\gcd(a_1,a_2,\dots,a_n)$,则 $\forall i \in [1,n], d \mid a_i$.
$\therefore d \mid \sum\limits_{i=1}^{n} a_i \times x_i$,即 $\gcd(a_1,a_2,\dots,a_n) \mid b$.
②充分性:
考虑数学归纳法.
Ⅰ. $n=2$ 时成立(详见 exgcd 部分).
Ⅱ. 假设 $n=k$ 时成立,考虑 $n=k+1$ 时,
设 $S=\sum\limits_{i=1}{k} a_i,d=\gcd(a_1,a_2,\dots,a_k)$.
则方程 $S \times X + a_{k+1} \times y = \gcd(d,a_{k+1})$ 有整数解.
即存在 $(X,Y)$ 使得 $\sum\limits_{i=1}^{k+1} a_i \times x_i = \gcd(a_1,a_2,\dots,a_{k+1})$ 一组解为 $x_i=X(i \in [1,k]),x_{k+1}=Y$.
$\therefore n=k+1$ 时成立.
综上,证毕.
二、欧拉定理
欧拉定理:$a^{\varphi(m)} \equiv 1 \pmod m(a \perp m)$.
证明:考虑所有与 $m$ 互素的数 $x_i(i \in [1,\varphi(m)]$,令 $p_i=ax_i$,不难得到 $\{p_1 \bmod m,p_2 \bmod m,\dots,p_{\varphi(m)} \bmod m\}$ 与 $\{x_1,x_2,\dots,x_{\varphi(m)}$ 一一对应.
累乘得:$\prod\limits_{i=1}^{\varphi(m)} p_i =\prod\limits_{i=1}^{\varphi(m)} x_i $,则 $a^{\varphi(m)} \equiv 1 \pmod m$.
扩展欧拉定理:当 $b \geq \varphi(m)$ 时,$a^b \equiv a^{b \bmod \varphi(m) + \varphi(m) \pmod m$.
证明考虑 $m$ 的每个素因子即可,此处不予给出.
三、exgcd
求解方程:$ax+by=c$ 或 $ax \equiv c \pmod b$.
当 $\gcd(a,b) \mid c$ 时原方程有解. 考虑先构造出方程 $ax+by=\gcd(a,b)$ 的解,再令 $x'=\frac{c}{\gcd(a,b)}\times x$ 即可,$y$ 同理.
设:$\begin{cases}ax_1+by_1=\gcd(a,b)\\bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b)\end{cases}$.
又 $\gcd(a,b)=\gcd(b,a \bmod b)$ 得,$ax_1+by_1=bx_2+(a \bmod b)y_2$.
将 $a\bmod b$ 展开并整理得:$ax_1+by_1=ay_2+b(x_2-\lfloor \frac{a}{b} \rfloor y_2)$.
不断递归处理即可. 边界条件为当 $b=0$ 时,$x=1,y=0$. 代码如下:

1 inline void exgcd ( int a,int b,int &x,int &y ) 2 { 3 if ( !b ) { x=1;y=0;return; } 4 exgcd(b,a%b,y,x);y-=a/b*x; 5 }
四、CRT/exCRT
求解方程组:$\begin{cases}x\equiv a_1 \pmod {n_1}\\x\equiv a_2 \pmod {n_2}\\ \dots \\x\equiv a_k \pmod {n_k}\end{cases}$. 其中 $\forall i \neq j, n_i \perp n_j$.
CRT(中国剩余定理):
① 令 $n=\prod\limits_{i=1}^{k} n_i$.
② 对于第 $i$ 个方程:
Ⅰ. 计算 $m_i=\frac{n}{n_i}$.
Ⅱ. 计算 $m_i$ 在 $\bmod n_i$ 意义下的逆元 $m_i^{-1}$. 即方程 $am_i+bn_i=1$ 的最小正整数解 $a$,可使用 exgcd 求解.
Ⅲ. 令 $c_i=m_im_i^{-1}$(不对 $n_i$ 取模).
③ 则唯一解为 $x \equiv \sum\limits_{i=1}^{k} a_ic_i \pmod n$.
证明直接代入即可.
求解方程组:$\begin{cases}x\equiv a_1 \pmod {n_1}\\x\equiv a_2 \pmod {n_2}\\ \dots \\x\equiv a_k \pmod {n_k}\end{cases}$. 其中 $n_i$ 为任意正整数.
exCRT:
考虑 $\begin{cases} x\equiv a_1 \pmod {n_1}\\x\equiv a_2 \pmod {n_2} \end{cases}$. 有:$\begin{cases} x=pn_1+a_1 \\ x=qn_2+a_2 \end{cases}$.
则 $n_1p-n_2q=a_2-a_1$. 用 exgcd 求出一组 $p,q$,则有:$x \equiv n_1p+a_1 \pmod {\operatorname{lcm} (n_1,n_2)}$. 两两合并即可.
来源:https://www.cnblogs.com/RenSheYu/p/12306837.html