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
