同步于 博客园洛谷博客

由于各网站之间 LaTeX\LaTeX 及 Markdown 渲染方式的差异,故不保证除博客园外的排版完好,对于排版有明显不正确的地方请以博客园为主。

质数

定义:无法被任何除了 11 及其本身的自然数整除的数。

性质:

  1. [1,n][1,n] 内大约有 nlnn\frac{n}{\ln n} 个质数,即每 lnn\ln n 个数就大约有一个质数。

判断质数

  1. 试除法

时间复杂度 O(n)\mathcal{O}(\sqrt{n})

1
2
3
4
5
6
7
bool isPrime(int n){
if (n<2) return 0;
int k=sqrt(n);
F(i,2,k)
if (!(n%i)) return 0;
return 1;
}

质数筛

  1. 埃氏筛

对于每个整数,显然其倍数都不是质数。

扫描所有数,每扫到一个没被标记的数,该数即为质数,随后标记该数的倍数为约数。

同时,对于当前的质数 xx,因为小于 x2x^2xx 的倍数已经被小于 xx 的质数扫描过了,因此只需要从 x2x^2 开始扫描倍数即可。

时间复杂度 O(nloglogn)\mathcal{O}(n\log \log n)

1
2
3
4
5
6
7
8
bool vis[N];
void EratosthenesSeive(int n){
F(i,2,n){
if (vis[i]) continue;
F(j,i,n/i) vis[i*j]=1;
}
return ;
}
  1. 线性筛(欧拉筛)

埃氏筛依旧会重复标记某些合数,考虑使合数被唯一标记。

viv_iii 的最小质因子,从前到后扫描所有数,若 vi=0v_i=0ii 为质数,使 vi=iv_i=i 并将 ii 加入到质数集 Prime\text{Prime}

对于每个数,扫描质数集的所有数 xx,若 xx 相比 viv_i 不是最小的质因子或 i×xi\times x 超出筛的范围则终止扫描,反之则 vi×x=xv_{i\times x}=x

时间复杂度 O(n)\mathcal{O}(n)

1
2
3
4
5
6
7
8
9
10
11
12
vector<int> prime;
int v[N];
void EulerSeive(int n){
F(i,2,n){
if (!v[i]) v[i]=i,prime.push_back(i);
V(x,prime){
if (x>v[i]||i*x>n) break;
v[i*x]=x;
}
}
return ;
}

质因数分解

前置知识

  1. 唯一分解定理:任意一个大于 11 的正整数 nn 都可被唯一分解为若干个质数的乘积,即 n=p1c1p2c2pkckn=p_1^{c_1}p_2^{c_2}\dots p_k^{c_k},其中 cic_i 为正整数,pip_i 为质数且 p1<p2<<pkp_1 < p_2 < \dots < p_k
1
2
3
4
5
6
7
8
9
10
11
12
int p[N],c[N],tot;
void Divide(int n){
int k=sqrt(n);
F(i,2,k){
if(!(n%i)){
p[++tot]=i,c[tot]=0;
while(!(n%i)) ++c[tot],n/=i;
}
}
if (n>1) p[++tot]=n,c[tot]=1;
return ;
}

约数

定义:若 nmodd=0n\bmod d=0,则称 ddnn 的约数,记作 dnd\mid n,读作 dd 整除 nn

求单个值的正约数集合

  1. 试除法

因为若 ddnn 的约数,则 nd\frac{n}{d} 也为 nn 的约数,所以除完全平方数的 n\sqrt n 之外,约数总是成对出现的。

故从 1n1\sim \sqrt n 判断 ii 是否整除 nn 即可,若整除且 inii\neq \frac{n}{i}ni\frac{n}{i} 也为约数。

推论:一个整数 nn 至多有 2n2\sqrt n 个约数。

时间复杂度 O(n)\mathcal{O}(\sqrt n)

1
2
3
4
5
6
7
8
9
10
11
vector<int> fac;
void FactorDivision(int n){
int k=sqrt(n);
F(i,1,k){
if (!(n%i)){
fac.push_back(i);
if (i!=n/i) fac.push_back(n/i);
}
}
return ;
}

求区间内的正约数集合

  1. 暴力+试除法

时间复杂度 O(nn)\mathcal{O}(n\sqrt n),代码见求单个值的正约数集合

  1. 倍数法

反过来考虑,运用埃氏筛的倍数标记思想,对于每个数 dddd 肯定为其的倍数的约数,故对于每个数,将这个数加入到这个数的倍数的正约数集合里。

时间复杂度 O(nlogn)\mathcal{O}(n\log n)

1
2
3
4
5
6
7
vector<int> fac[N];
void FactorDivision(int n){
F(i,1,n)
F(j,1,n/i)
fac[i*j].push_back(i);
return ;
}

数论分块

例题:给定正整数 nn,求 i=1nni\sum_{i=1}^{n}\lfloor \frac{n}{i} \rfloor,其中 1n1091\leq n\leq 10^9

显然 ni\frac{n}{i} 在连续的区间内值相等,故可以分成若干块再进行计算。

对于每个块,设 ll 为该块的左边界,那么这个块的值为 k=nlk=\lfloor \frac{n}{l} \rfloor,那么右边界 rr 即为最大的 ii,且满足 inki\leq \frac{n}{k},故 r=nkr=\lfloor \frac{n}{k} \rfloor,代入 kkr=nnlr=\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor

时间复杂度 O(n)\mathcal{O}(\sqrt n)

1
2
3
4
5
6
7
8
9
int mathBlock(int n){
int l=1,r,ans=0;
while(l<=n){
r=n/(n/l);
ans+=(n/l)*(r-l+1);
l=r+1;
}
return ans;
}

题目

最大公约数 最小公倍数

前置知识

  1. 公约数:若 xax\mid axbx\mid b,则称 xxaabb 的公约数。

  2. 公倍数:若 axa\mid xbxb\mid x,则称 xxaabb 的公倍数。

显然最大公约数就是公约数中最大的数字,最小公倍数就是公倍数中最小的数字。

求最大公约数

  1. 欧几里得算法

gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b,a\bmod b)

a>ba > ba=bk+ca=bk+c,则有 c=amodbc=a\bmod b
da,dbd\mid a,d\mid b,则有:

c=abkcd=adbdk\begin{aligned} c=a-bk\\ \frac{c}{d}=\frac{a}{d}-\frac{b}{d}k \end{aligned}

由对 dd 的定义可知 dcd\mid c,故 gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b,a\bmod b),得证。

时间复杂度 O(logn)\mathcal{O}(\log n)

1
2
3
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
  1. 更相减损术

gcd(a,b)=gcd(ab,b)\gcd(a,b)=\gcd(a-b,b)

a=ba=b 时,显然 gcd(a,b)=a=b\gcd(a,b)=a=b
aba\geq b,那么 da,db\forall d\mid a,d\mid bdabd\mid a-b
aabb 的所有公约数都为 aba-bbb 的公约数,gcd(a,b)=gcd(ab,b)\gcd(a,b)=\gcd(a-b,b),得证。

时间复杂度 O(max(a,b))\mathcal{O}(\max(a,b))

1
2
3
4
5
6
7
int gcd(int a,int b){
while(a!=b){
if (a>b) a-=b;
else b-=a;
}
return a;
}
  1. 更相减损术优化

2a2\mid a2b2\mid b,则 gcd(a,b)=2gcd(a2,b2)\gcd(a,b)=2\gcd(\frac{a}{2},\frac{b}{2})

反之,则若 2a2\mid a2b2\nmid b,则 gcd(a,b)=gcd(a2,b)\gcd(a,b)=\gcd(\frac{a}{2},b)

2a,2b2\nmid a,2\mid b 时同理。

时间复杂度 O(logn)\mathcal{O}(\log n)

1
2
3
4
5
6
7
8
9
10
11
12
13
int gcd(int a,int b){
int aa=0,bb=0;
while(!(a&1)) a>>=1,++aa;
while(!(b&1)) b>>=1,++bb;
while(1){
while(!(a&1)) a>>=1;
while(!(b&1)) b>>=1;
if (a==b) break;
if (a<b) swap(a,b);
a-=b;
}
return a<<min(aa,bb);
}

求最小公倍数

gcd(a,b)×lcm(a,b)=a×b\gcd(a,b)\times \operatorname{lcm}(a,b)=a\times b

唯一分解定理,可使 a=p1ca1p2ca2pkcak,b=p1cb1p2cb2pkcbka=p_1^{c_{a_1}}p_2^{c_{a_2}}\dots p_k^{c_{a_k}},b=p_1^{c_{b_1}}p_2^{c_{b_2}}\dots p_k^{c_{b_k}}
则最大公约数为 p1min(ca1,cb1)p2min(ca2,cb2)pkmin(cak,cbk)p_1^{\min(c_{a_1},c_{b_1})}p_2^{\min(c_{a_2},c_{b_2})}\dots p_k^{\min(c_{a_k},c_{b_k})},最小公倍数为 p1max(ca1,cb1)p2max(ca2,cb2)pkmax(cak,cbk)p_1^{\max(c_{a_1},c_{b_1})}p_2^{\max(c_{a_2},c_{b_2})}\dots p_k^{\max(c_{a_k},c_{b_k})}
由于 min(ca,cb)+max(ca,cb)=ca+cb\min(c_a,c_b)+\max(c_a,c_b)=c_a+c_b,所以 gcd(a,b)×lcm(a,b)=a×b\gcd(a,b)\times \operatorname{lcm}(a,b)=a\times b,得证。

时间复杂度为求最大公约数的时间复杂度。

1
2
3
4
int lcm(int a,int b){
// 先除后乘可防止爆 int。
return a/gcd(a,b)*b;
}

欧拉函数

前置知识

  1. 互质x,yN\forall x,y\in \mathbb{N},若 gcd(x,y)=1\gcd(x,y)=1,则称 xxyy 互质,记作 xyx\perp y

  2. 积性函数:若 x,yx,y 互质且 f(x)×f(y)=f(xy)f(x)\times f(y)=f(xy),则称函数 ff 为积性函数。

  3. 完全积性函数:若 x,yR\forall x,y\in \mathbb{R}f(x)×f(y)=f(xy)f(x)\times f(y)=f(xy),则称 ff 为完全积性函数。

定义n>1\forall n>1i=1n[in]\sum_{i=1}^{n}[i\perp n],记作 φ(n)\varphi(n),特别地,φ(1)=1\varphi(1)=1

性质

  1. nn 为质数,则 φ(n)=n1\varphi(n)=n-1

  2. n>1\forall n>1i=1n[in]×i=n2×φ(n)\sum_{i=1}^{n}[i\perp n]\times i=\frac{n}{2}\times \varphi(n)

gcd(n,x)=gcd(n,nx)\because \gcd(n,x)=\gcd(n,n-x)

\thereforexx 不与 nn 互质,则 nxn-x 也不与 nn 互质,且 xxnxn-x 总成对出现,其平均值为 n2\frac{n}{2}

\thereforenn 互质的数的平均值为 n2\frac{n}{2},而与 nn 互质的数有 φ(n)\varphi(n) 个,得证。

  1. φ\varphi 为积性函数,即若 x,yx,y 互质,则 φ(xy)=φ(x)×φ(y)\varphi(xy)=\varphi(x)\times \varphi(y)

  2. n=pkn=p^k,且 pp 为质数,则 φ(n)=(p1)×pk1\varphi(n)=(p-1)\times p^{k-1}

根据唯一分解定理,可知 nnpp 的倍数都不互质,与其它数均互质。

pp 的倍数有 pk1p^{k-1} 个(含 nn 本身),故 φ(n)=pkpk1=(p1)×pk1\varphi(n)=p^k-p^{k-1}=(p-1)\times p^{k-1},得证。

  1. pp 为质数,且 ppnn 的约数,则 φ(np)=φ(n)×p\varphi(np)=\varphi(n)\times p

因为 pp 为质数,所以 npnpnn 的质因子相同,仅 pp 的指数不同,故(计算公式见求单个值的欧拉函数):

φ(np)φ(n)=np×i=1k(11pi)n×i=1k(11pi)=npn=p\frac{\varphi(np)}{\varphi{(n)}}=\frac{np\times\prod_{i=1}^{k}(1-\frac{1}{p_i})}{n\times \prod_{i=1}^{k}(1-\frac{1}{p_i})}=\frac{np}{n}=p

所以 φ(np)=φ(n)×p\varphi(np)=\varphi(n)\times p,得证。

  1. pp 为质数且 pp 不为 nn 的约数,则 φ(np)=φ(n)×(p1)\varphi(np)=\varphi(n)\times (p-1)

因为 pp 不为 nn 的约数,所以 pnp\perp n
φ(np)=φ(n)×φ(p)=φ(n)×(p1)\varphi(np)=\varphi(n)\times \varphi(p)=\varphi(n)\times (p-1),得证。

  1. dnφ(d)=n\sum_{d\mid n}\varphi(d)=n

f(x)=dnφ(d)f(x)=\sum_{d\mid n}\varphi(d),因为 φ\varphi 为积性函数,所以若 a,ba,b 互质,则 f(ab)=dabφ(d)=(daφ(d))×(dbφ(d))f(ab)=\sum_{d\mid ab}\varphi(d)=(\sum_{d\mid a}\varphi(d))\times (\sum_{d\mid b}\varphi(d)),所以 ff 为积性函数。
对于一个质因子 pkp^kf(pk)=dpkφ(d)=φ(1)+φ(p)++φ(pk)f(p^k)=\sum_{d\mid p^k}\varphi(d)=\varphi(1)+\varphi(p)+\dots+\varphi(p^k),结果为等比数列求和加 11,结果为 pkp^k
f(n)=i=1kf(pici)=i=1kpici=nf(n)=\prod_{i=1}^{k}f(p_i^{c_i})=\prod_{i=1}^{k}p_i^{c_i}=n,得证。

求单个值的欧拉函数

通过唯一分解定理,使 n=p1c1×p2c2××pkckn=p_1^{c_1}\times p_2^{c_2}\times \dots \times p_k^{c_k},则:

φ(n)=n×p11p1×p21p2×pk1pk=n×i=1k(11pi)\varphi(n)=n\times \frac{p_1-1}{p_1}\times \frac{p_2-1}{p_2}\times \dots \frac{p_k-1}{p_k}=n\times \prod_{i=1}^{k}(1-\frac{1}{p_i})

对于 nn 的所有质因子 pip_i,在 1n1\sim n 范围内 pip_i 的倍数有 npi\frac{n}{p_i} 个。
那么对于 nn 的两个质因子 p,qp,q,根据容斥,1n1\sim n 范围内含质因子 ppqq 的数的个数为 np+nqnpq\frac{n}{p}+\frac{n}{q}-\frac{n}{pq}
不含质因子 ppqq 的数的个数为 nnpnq+npq=n×(11p1q+1pq)=n(11p)(11q)n-\frac{n}{p}-\frac{n}{q}+\frac{n}{pq}=n\times (1-\frac{1}{p}-\frac{1}{q}+\frac{1}{pq})=n(1-\frac{1}{p})(1-\frac{1}{q}),将公式扩展到所有质因子 pip_i 即可得证。

故在分解质因数时即可求出 φ\varphi,时间复杂度 O(n)\mathcal O(\sqrt n)

1
2
3
4
5
6
7
8
9
10
11
int Phi(int n){
int ans=n,k=sqrt(n);
F(i,2,k){
if (!(n%i)){
ans=ans/i*(i-1);
while (!(n%i)) n/=i;
}
}
if (n>1) ans=ans/n*(n-1);
return ans;
}

求区间内的欧拉函数

  1. 暴力

代码见求单个数的欧拉函数,时间复杂度 O(nn)\mathcal O(n\sqrt n)

  1. 埃氏筛

时间复杂度 O(nlogn)\mathcal O(n\log n)

1
2
3
4
5
6
7
8
9
10
int phi[N];
void CalculatePhi(int n){
F(i,1,n) phi[i]=i;
F(i,2,n){
if (phi[i]==i)
for (int j=i;j<=n;j+=i)
phi[j]=phi[j]/i*(i+1);
}
return ;
}
  1. 线性筛

时间复杂度 O(n)\mathcal O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int phi[N],v[N];
vector<int> prime;
void CalculatePhi(int n){
phi[1]=1;
F(i,2,n){
if (!v[i])
v[i]=i,phi[i]=i-1,prime.push_back(i);
V(x,prime){
if (x>v[i]||i*x>n) break;
v[i*x]=x;
phi[i*x]=phi[i]*(i%x?x-1:x);
}
}
return ;
}

题目

莫比乌斯函数

定义:若根据唯一分解定理使正整数 n=p1c1p2c2pkckn=p_1^{c_1}p_2^{c_2}\dots p_k^{c_k},则有:

μ(n)={0,i[1,k],ci>11,m0(mod2),i[1,m],ci=11,m1(mod2),i[1,m],ci=1\mu(n)= \begin{cases} 0,\exists i \in [1,k],c_i > 1\\ 1,m\equiv 0\pmod 2,\forall i \in [1,m],c_i=1\\ -1,m\equiv 1\pmod 2,\forall i \in [1,m],c_i=1 \end{cases}

特别地,μ(1)=1\mu(1)=1

求单个值的莫比乌斯函数

  1. 质因数分解

时间复杂度 O(n)\mathcal{O}(\sqrt n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int Mobius(int n){
if (n==1) return 1;
int k=sqrt(n),cnt=0;
F(i,2,k){
if (!(n%i)){
int ci=0;
while(!(n/i)) n/=i,++ci;
if (ci>1) return 0;
++cnt;
}
}
if (n>1) ++cnt;
return cnt&1?-1:1;
}

求区间内的莫比乌斯函数

  1. 埃氏筛

先初始化 i[1,n],μ(i)=1\forall i\in [1,n],\mu(i)=1,显然对于质数 nnμ(n)=1\mu(n)=-1,对于 nn 的倍数,若 n2knn^2\mid kn,则说明出现了 ci>1c_i > 1 的情况,μ(kn)=0\mu(kn)=0,反之则 μ(kn)=μ(kn)\mu(kn)=-\mu(kn)

时间复杂度 O(nloglogn)\mathcal{O}(n\log \log n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int mu[N];
bool vis[N];
void Mobius(int n){
fill(mu+1,mu+1+n,1);
F(i,2,n){
if (vis[i]) continue;
mu[i]=-1;
for (int j=(i<<1);j<=n;j+=i){
v[j]=1;
if (!((j/i)%i)) mu[i]=0;
else mu[i]*=-1;
}
}
return ;
}
  1. 线性筛

对于质数 nn 及每一个被遍历到的合数 n×vin\times v_i,若其出现 ci>1c_i > 1,则停止遍历,否则 μ(n×vi)=μ(n)\mu(n\times v_i)=-\mu(n)

时间复杂度 O(n)\mathcal{O}(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vector<int> prime;
int mu[N];
bool vis[N];
void Mobius(int n){
mu[1]=1;
F(i,2,n){
if (!vis[i]) vis[i]=1,mu[i]=-1,prime.push_back(i);
V(x,prime){
if (i*x>n) break;
vis[i*x]=1;
if (!(i%x)) break;
mu[i*x]=-mu[i];
}
}
return ;
}

同余

前置知识

  1. 取模的定义式:

amodp={aap×p,a0(amodp),a<0a\bmod p= \begin{cases} a-\lfloor \frac{a}{p} \rfloor \times p,a\geq 0\\ -(-a\bmod p),a<0 \end{cases}

定义:若 xmodp=ymodpx\bmod p=y\bmod p,则称 x,yx,ypp 同余,记作 xy(modp)x\equiv y\pmod p

性质

  1. 自反性xx(modp)x\equiv x\pmod p

  2. 对称性:若 xy(modp)x\equiv y\pmod p,则 yx(modp)y\equiv x\pmod p

  3. 传递性:若 xy(modp)x\equiv y\pmod pyz(modp)y\equiv z\pmod p,则 xz(modp)x\equiv z\pmod p

  4. 同余式相加性:若 xy(modp)x\equiv y\pmod pab(modp)a\equiv b\pmod p,则 (x±a)(y±b)(modp)(x\pm a)\equiv (y\pm b)\pmod p

  5. 同余式相乘性:若 xy(modp)x\equiv y\pmod pab(modp)a\equiv b\pmod p,则 axby(modp)ax\equiv by\pmod p

乘法逆元

前置知识

  1. 费马小定理:若 pp 为质数且 apa\perp p,则 apa(modp)a^p\equiv a\pmod p

定义:若 apa\perp pxxax1(modp)ax\equiv 1\pmod p 的解,则称 xxaapp 的乘法逆元,记作 a1(modp)a^{-1}\pmod p

求单个值的乘法逆元

a⊥̸pa\not \perp p,则不存在 aapp 的乘法逆元。

  1. 快速幂:pp 为质数,且 apa\perp p,根据费马小定理,可知 aapp 的乘法逆元为 ap2modpa^{p-2}\bmod p

由费马小定理可知 ap11(modp)a^{p-1}\equiv 1\pmod p
而我们要求 ax1(modp)ax\equiv 1\pmod p 的解,由传递性可得:

axap1(modp)xap2(modp)\begin{aligned} ax\equiv a^{p-1}\pmod p\\ x\equiv a^{p-2}\pmod p \end{aligned}

得证。

  1. 扩展欧几里得算法:将方程改写为 ax+py=1ax+py=1,使用扩展欧几里得算法求出 xx 即可。

题目

求区间内的乘法逆元

  1. 暴力+快速幂

仅适用于 pp 为质数且 apa\perp p 的情况,时间复杂度 O(nlogp)\mathcal{O}(n\log p)

  1. 暴力+扩展欧几里得算法

代码见扩展欧几里得算法,时间复杂度 O(nlogp)\mathcal{O}(n\log p)

  1. 线性递推

invi=pi×invpmodimodpinv_i=-\lfloor \frac{p}{i} \rfloor \times inv_{p\bmod i}\bmod p,特别地,inv1=1inv_1=1

p=k×i+rp=k\times i+r,其中 ii 为当前的数,kkpi\lfloor \frac{p}{i} \rfloorrrpmodip\bmod i

p0(modp)k×i+r0(modp)\begin{aligned} p \equiv 0 \pmod p\\ k\times i+r \equiv 0 \pmod p \end{aligned}

两边同乘 i1r1i^{-1}r^{-1} 得:

k×r1+i10(modp)i1k×r1(modp)i1pi×(pmodi)1(modp)\begin{aligned} k\times r^{-1}+i^{-1} \equiv 0 \pmod p\\ i^{-1} \equiv -k\times r^{-1}\pmod p\\ i^{-1} \equiv -\lfloor \frac{p}{i} \rfloor \times {(p\bmod i)}^{-1} \pmod p \end{aligned}

故递推即可,得证。

时间复杂度 O(n)\mathcal{O}(n)

1
2
inv[1]=1;
F(i,2,n) inv[i]=(p-p/i)*inv[p%i]%p;

题目

求区间内阶乘的乘法逆元

  1. 线性递推

facinvi=facinvi+1×(i+1)facinv_i=facinv_{i+1}\times (i+1),特别地,facinvn=invn!modpfacinv_n=inv_{n!\bmod p}

facinvi+1=1(i+1)!\because facinv_{i+1}=\frac{1}{(i+1)!}
facinvi+1×(i+1)=1i!=facinvi\therefore facinv_{i+1}\times (i+1)=\frac{1}{i!}=facinv_i
得证。

时间复杂度 O(n)\mathcal{O}(n)

欧拉定理

前置知识

  1. 同余类a[0,m1]\forall a\in[0,m-1],集合 {a+km}(kZ)\{a+km\}(k\in \mathbb{Z}) 的所有数模 mm 同余,余数均为 aa,则称该集合为一个模 mm 的同余类,记为 a\overline{a}

  2. 完全剩余系mm 个模 mm 的同余类 i(i[0,m1])\overline{i} (i\in[0,m-1]) 所构成的集合称为完全剩余系。

  3. 简化剩余系φ(m)\varphi(m) 个与 mm 互质的数的同余类所构成的集合称为简化剩余系。

  4. 费马小定理:见乘法逆元

定义:若 apa\perp p,则 aφ(p)1(modp)a^{\varphi(p)}\equiv 1\pmod p

扩展欧拉定理

ab{abmodφ(p),apab,a⊥̸p,b<φ(p)abmodφ(p)+φ(p),a⊥̸p,bφ(p)(modp)a^b\equiv \begin{cases} a^{b\bmod \varphi(p)},a\perp p\\ a^b,a\not \perp p,b<\varphi(p)\\ a^{b\bmod \varphi(p)+\varphi(p)},a\not \perp p,b\geq \varphi(p) \end{cases} \pmod p

题目

扩展欧几里得算法

前置知识

  1. 欧几里得算法:见求最大公约数

  2. 裴蜀定理(Bézout 定理)a,bZ,x,yZ,ax+by=gcd(a,b)\forall a,b\in \mathbb{Z},\exists x,y\in \mathbb{Z},ax+by=\gcd(a,b)

在欧几里得算法的最后一步,即 b=0b=0 时,显然存在 x=1,y=0x=1,y=0 使 a×1+0×0=gcd(a,0)a\times 1+0\times 0=\gcd(a,0)
考虑当 b>0b>0 时,gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b,a\bmod b),设存在一对整数 x,yx,y 使得 gcd(b,amodb)=bx+(amodb)y\gcd(b,a\bmod b)=bx+(a\bmod b)y
由取模的定义式,可转化 bx+(amodb)y=bx+(abab)y=ay+b(xaby)bx+(a\bmod b)y=bx+(a-b\lfloor \frac{a}{b} \rfloor)y=ay+b(x-\lfloor \frac{a}{b} \rfloor y)
故当 x=y,y=xabyx^{\prime}=y,y^{\prime}=x-\lfloor \frac{a}{b} \rfloor y 时,有 ax+by=gcd(a,b)ax^{\prime}+by^{\prime}=\gcd(a,b),对递归过程使用数学归纳法即可得证。

对于求 ax+by=gcd(a,b)ax+by=\gcd(a,b)x,yx,y 的值,由裴蜀定理的证明中可知仅需要在欧几里得算法中保存 x,yx,y 即可。

1
2
3
4
5
6
7
8
9
10
11
int exgcd(int a,int b,int &x,int &y){
if (!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int z=x;
x=y;
y=z-y*(a/b);
return d;
}

题目

中国剩余定理

前置知识

  1. 乘法逆元:见乘法逆元

p1,p2,,pnp_1,p_2,\dots,p_n 两两互质,求解方程组:

{xa1(modp1)xa2(modp2)xan(modpn)\begin{cases} x\equiv a_1\pmod {p_1}\\ x\equiv a_2\pmod {p_2}\\ \vdots \\ x\equiv a_n\pmod {p_n} \end{cases}

m=i=1npim=\prod_{i=1}^{n}{p_i}Mi=mpiM_i=\frac{m}{p_i}tit_iMiM_ipip_i 的乘法逆元,则 x=i=1naiMitimodmx=\sum_{i=1}^{n}{a_iM_it_i}\bmod m

Mi\because M_i 为除 pip_i 外所有模数的倍数且 p1,p2,,pnp_1,p_2,\dots,p_n 两两互质。
ki,akMktk0(modpk)\therefore \forall k\neq i,a_kM_kt_k\equiv 0\pmod {p_k}
ti(Mi)1(modpi)\because t_i\equiv(M_i)^{-1}\pmod {p_i}
aiMitiai(modpi)\therefore a_iM_it_i\equiv a_i\pmod {p_i}
由同余式相加性且 m=i=1npim=\prod_{i=1}^{n}{p_i} 可知 xx 在展开之后即为 i=1naiMitimodm\sum_{i=1}^{n}{a_iM_it_i}\bmod m,得证。

时间复杂度 O(nlogp)\mathcal{O}(n\log p)

1
2
3
4
5
6
7
8
int CRT(int n){
F(i,1,n){
exgcd(M/p[i],p[i],x,y);
x=(x%p[i]+p[i])%p[i];
ans+=x*a[i]*(M/p[i]),ans%=M;
}
return ans;
}

题目

扩展中国剩余定理

前置知识

  1. 扩展欧几里得算法:见扩展欧几里得算法

p1,p2,,pnp_1,p_2,\dots,p_n 不保证两两互质,则上述结论不成立,需要另外一种解法。

考虑 n=2n=2 的情况,可转化为不定方程 x=kp1+a1=bp2+a2,k,bZx=kp_1+a_1=bp_2+a_2,k,b\in \mathbb{Z},则有 kp1bp2=a2a1kp_1-bp_2=a_2-a_1

a2a1gcd(p1,p2)a_2-a_1 \nmid \gcd(p_1,p_2),则无解,反之可用扩展欧几里得算法求出一组解 (k,b)(k,b)

xkp1+a1(modlcm(p1,p2))x\equiv kp_1+a_1\pmod {\operatorname{lcm} (p_1,p_2)}

扩展到 n2n\neq 2 的情况,仅需要依次求解即可。

时间复杂度 O(nlogn)\mathcal{O}(n\log n)

1
2
3
4
5
6
7
8
9
10
11
12
int exCRT(int n){
int ret=a[1],cur=p[1],x,y;
F(i,2,n){
int gcd=exgcd(cur,p[i],x,y),c=((a[i]-ret)%p[i]+p[i])%p[i];
if (c%gcd) return -1;
x=quick_mul(x,c/gcd,p[i]/gcd);
ret+=x*cur;
cur=cur/gcd*p[i];
ret=(ret%cur+cur)%cur;
}
return (ret%cur+cur)%cur;
}

题目

组合

加法原理 乘法原理

加法原理(分类):一个问题有 nn 方法,每个类别有 aia_i 个不同的方法,那么解决此问题有 a1+a2++ana_1+a_2+\dots+a_n 个方法。

乘法原理(分布):一个问题有 nn 方法,每个方法有 aia_i 个不同的方式,那么解决此问题的方式个数为 a1×a2××ana_1\times a_2\times \dots \times a_n

容斥原理

n=2n=2 时,AB=A+BAB|A\cup B|=|A|+|B|-|A\cap B|

n=3n=3 时,ABC=A+B+CABACBC+ABC|A\cup B\cup C|=|A|+|B|+|C|-|A\cap B|-|A\cap C|-|B\cap C|+|A\cap B\cap C|

通项式i=1nAi=J={1,2,,n}(1)J1jJAj|\bigcup_{i=1}^{n}A_i|=\sum_{\varnothing \neq J=\subseteq\{1,2,\dots,n\}}(-1)^{|J|-1}|\bigcap_{j\in J}A_j|

德摩根定律

AB=AB\overline{A\cup B}=\overline{A}\cap \overline{B}AB=AB\overline{A\cap B}=\overline{A}\cup \overline{B}

简记:交之补等于补之并,并之补等于补之交。

推论i=1nAi=J{1,2,,n}(1)JjJAj|\bigcap_{i=1}^{n}\overline{A_i}|=\sum_{\varnothing \neq J\subseteq \{1,2,\dots,n\}}(-1)^{|J|}|\bigcap_{j\in J} A_j|

i=1nAi=J={1,2,,n}(1)J1jJAji=1nAi=J{1,2,,n}(1)JjJAji=1nAi=J{1,2,,n}(1)JjJAj\begin{aligned} |\bigcup_{i=1}^{n}A_i|=\sum_{\varnothing \neq J=\subseteq\{1,2,\dots,n\}}(-1)^{|J|-1}|\bigcap_{j\in J}A_j|\\ |\bigcap_{i=1}^{n}\overline{A_i}|=\sum_{\varnothing \neq J\subseteq \{1,2,\dots,n\}}(-1)^{|J|}|\bigcap_{j\in J} A_j|\\ |\bigcap_{i=1}^{n}{A_i}|=\sum_{\varnothing \neq J\subseteq \{1,2,\dots,n\}}(-1)^{|J|}|\bigcap_{j\in J} \overline{A_j}| \end{aligned}

排列数 组合数

排列数Ank=n!(nk)!A_{n}^{k}=\frac{n!}{(n-k)!}

组合数Cnk=(nk)=Ankk!=n!(nk)!k!=n×(n1)××(nk+1)k!=i=0k1(ni)k!C_{n}^{k}=\binom{n}{k}=\frac{A_{n}^{k}}{k!}=\frac{n!}{(n-k)!k!}=\frac{n\times(n-1)\times \dots \times (n-k+1)}{k!}=\frac{\prod_{i=0}^{k-1}{(n-i)}}{k!}

特别地,若 k>nk>nAnk=Cnk=0A_{n}^{k}=C_{n}^{k}=0

性质

  1. Cn0=1C_{n}^{0}=1Cn1=nC_{n}^{1}=nCn2=n(n1)2C_{n}^{2}=\frac{n(n-1)}{2}

  2. Cnk=CnnkC_{n}^{k}=C_{n}^{n-k}

  3. Cnk=Cn1k+Cn1k1C_{n}^{k}=C_{n-1}^{k}+C_{n-1}^{k-1}

  4. i=0nCni=2n\sum_{i=0}^{n}C_{n}^{i}=2^n

求组合数

  1. 暴力
1
2
3
4
5
6
int C(int n,int k,int p){
int a=1,b=1;
F(i,0,k-1) a*=(n-i);
F(i,1,k) b*=i;
return (a/b)%p;
}

时间复杂度 O(Tn)\mathcal{O}(Tn),但适用性极差(易爆 long long)。

  1. 递推预处理

Ci,j={1,j=0Ci1,j+Ci1,j1,otherwiseC_{i,j}= \begin{cases} 1,j=0\\ C_{i-1,j}+C_{i-1,j-1},\text{otherwise} \end{cases}

由性质 33 得证。

1
2
3
4
5
6
7
void InitC(int n,int p){
F(i,0,n){
C[i][0]=1;
F(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
}
return ;
}

时间复杂度 O(n2+T)\mathcal{O}(n^2+T)

  1. 预处理阶乘+逆元

这里使用了快速幂求逆元。

1
2
3
4
int C(int n,int k,int p){
if (!k) return 1;
return (fac[n]*ksm(fac[n-k],p-2,p)%p*ksm(fac[k],p-2,p)%p)%p;
}

时间复杂度 O(n+Tlogp)\mathcal{O}(n+T\log p)

  1. 卢卡斯定理

卢卡斯定理仅限 pp 为质数时适用,时间复杂度 O(p+T×logn)\mathcal{O}(p+T\times \log n)

  1. 扩展卢卡斯定理

pp 不为质数时也适用,时间复杂度 O(p+Tlogp)\mathcal{O}(p+T\log p)

二项式定理

(x+y)n=Cn0xny0+Cn1xn1y1++Cnnx0yn=i=0nCnixniyi(x+y)^{n}=C_{n}^{0}x^ny^0+C_{n}^{1}x^{n-1}y^{1}+\dots+C_{n}^{n}x^0y^n=\sum_{i=0}^{n}C_{n}^{i}x^{n-i}y^i

对于每一个 (x+y)(x+y),显然要么选 xx,要么选 yy
若有 kk(x+y)(x+y) 选择 yy,则有 nkn-k 个选择 xx,由于要选取 kk(x+y)(x+y),故系数为 CnkC_{n}^{k}
因为 kk 为分类,所以利用加法原理,得证。

计数技巧

等价替代

对于某些计数题,正面解决可能会很困难,这时可以用一些方法将问题转化为另一种更为容易计算的问题。

捆绑法

要求某些物品相邻,则可将所需相邻的物品看做一个整体,然后计算排列,再分别计算每个整体内的排列相乘。

例题:有 ABCDE\text{ABCDE} 五种物品,其中要求 AB\text{AB} 相邻,CD\text{CD} 相邻,求排列方案数。

可先将 AB\text{AB}CD\text{CD} 看作一个整体,然后对剩下的 33 个物品进行排列,有 A33=6A_{3}^{3}=6 种排列方案。
然后再计算各个整体内的排列,A22=2A_{2}^{2}=2 种。
故共有 2×A22×A33=242\times A_{2}^{2}\times A_{3}^{3}=24 种排列方案。

插空法

要求某些物品两两不相邻,则可先将其它的物品排好,然后再从空当中插入,分别计算排列后相乘。

例题:有 ABCDEFG\text{ABCDEFG} 其中物品,其中要求 ABC\text{ABC} 两两不相邻,求排列方案数。

先对其它 44 种物品进行排列,有 A44=24A_{4}^{4}=24 种方案。
然后将剩下 33 种物品插入到其中的 55 个空当中,有 A53=60A_{5}^{3}=60 种方案,故共有 A44×A53=1440A_{4}^{4}\times A_{5}^{3}=1440 种排列方案。

隔板法

至少分配问题不定方程整数解问题可转化为关于插板的组合问题。

例题 11:将 nn 个物品分给 kk 个人,要求每个人至少被分配到一个,求方案数。

nn 件物品排成一行,中间有 k1k-1 个隔板,表示每个人分到的部分,而 k1k-1 个隔板只能插入到 n1n-1 个空当中,故方案数为 Cn1k1C_{n-1}^{k-1}

例题 22:将例题 11 的限制改为“允许有人被分配到 00 个”,求方案数。

可将题意抽象为数学模型,求方程 i=1kxi=n\sum_{i=1}^{k}x_i=n 的解,其中 xi1x_i\geq 1
若改限制为 x0x\geq 0,则设 yi=xi+1y_i=x_i+1,转化为 i=1k=n+k\sum_{i=1}^{k}=n+k
再结合上题的解,即可求出方案数为 Cn+k1k1C_{n+k-1}^{k-1}

改变计数目标

对于某些计数题,直接解决可能会很困难,可以用减法原理或容斥原理等内容转化为容易求的问题。

改变枚举顺序

对于某些计数题,直接枚举只能拿到暴力分,可以改变枚举顺序使其更容易计算。

例题:求 i=1nj=1nmax(i,j)\sum_{i=1}^{n}\sum_{j=1}^{n}\max(i,j)

i=1nj=1nmax(i,j)=k=1nki=1nj=1n[max(i,j)=k]=k=1nk(2k1)=2k=1nk2(n+1)n2=4n3+3n2n6\begin{aligned} \sum_{i=1}^{n}\sum_{j=1}^{n}\max(i,j) &=\sum_{k=1}^{n}k\sum_{i=1}^{n}\sum_{j=1}^{n}[\max(i,j)=k]\\ &=\sum_{k=1}^{n}k(2k-1)\\ &=2\sum_{k=1}^{n} k^2 -\frac{(n+1)n}{2}\\ &=\frac{4n^3+3n^2-n}{6} \end{aligned}

多重集的排列组合数

多重集:指包含重复元素的广义集合。

多重集的排列数

S={n1a1,n2a2,,nkak}S=\{n_1a_1,n_2a_2,\dots,n_ka_k\} 为多重集,则 SS 的全排列个数为

(i=1ki)!i=1kni!\frac{(\sum_{i=1}^{k} i)!}{\prod_{i=1}^{k}n_i!}

多重集的组合数

S={n1a1,n2a2,,nkak}S=\{n_1a_1,n_2a_2,\dots,n_ka_k\} 为多重集,从 SS 中取出 r(rmin(ni))r(r\leq \min(n_i)) 个元素组成一个多重集,不考虑元素顺序,产生不同的多重集的数量为

Ck+r1k1C_{k+r-1}^{k-1}

设对于每一个 aia_i 选取 xix_i 个,则问题可转化为 i=1kxi=r(xi0)\sum_{i=1}^{k}x_i=r(x_i\geq 0) 的不定方程。
用隔板法求解即可。

卢卡斯定理

模数 pp 必须为质数

Cnmmodp=Cnpmp×CnmodpmmodpmodpC_{n}^{m}\bmod p=C_{\frac{n}{p}}^{\frac{m}{p}}\times C_{n\bmod p}^{m\bmod p}\bmod p

其中对于左半部分进行递归,右半部分直接进行计算,因为 nmodp,mmodp[0,p1]n\bmod p,m\bmod p\in [0,p-1]

时间复杂度 O(p+T×logn)\mathcal{O}(p+T\times \log n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int fac[N],facinv[N];
void Init(int n,int p){
fac[0]=1;
F(i,1,n) fac[i]=fac[i-1]*i,fac[i]%=p;
facinv[n]=ksm(fac[n],p-2,p);
FJ(i,n-1,0) facinv[i]=facinv[i+1]*(i+1),facinv[i]%=p;
return ;
}
int C(int n,int m,int p){
if (m>n) return 0;
return ((fac[n]*facinv[m])%p*facinv[n-m])%p;
}
int Lucas(int n,int m,int p){
if (!m||n==m) return 1;
return (Lucas(n/p,m/p,p)*C(n%p,m%p,p))%p;
}
int Solve(int n,int m,int p){
Init(p-1,p);
return Lucas(n,m,p);
}

卡特兰数

定义:一种常出现于各种计数问题的数列,记作 HnH_n,特别地,H0=H1=1H_0=H_1=1

卡特兰数列

nn 00 11 22 33 44 55 66 \dots
HnH_n 11 11 22 55 1414 4242 132132 \cdots

常见题型

  1. nn 个左括号和 nn 个右括号组成的合法括号序列的数量?HnH_n
  2. 入栈序列为 1,2,,n1,2,\dots,n 的合法出栈序列数量?HnH_n
  3. nn 个节点构成的不同的二叉树的数量?HnH_n
  4. 只能向上或向右走,能从 (0,0)(0,0) 走到 (n,n)(n,n) 且不接触直线 y=xy=x 的路线的数量?2Hn12H_{n-1}
  5. 只能向上或向右走,能从 (0,0)(0,0) 走到 (n,n)(n,n) 且不穿过直线 y=xy=x 的路线的数量?Hn+1H_{n+1}

求卡特兰数

以下所有公式均有 i2i\geq 2

  1. 递推

Hi=Hi1(4i2)i+1H_i=\frac{H_{i-1}(4i-2)}{i+1}

  1. 组合通式

Hi=C2iii+1H_i=\frac{C_{2i}^{i}}{i+1}

Hi=C2iiC2ii1H_i=C_{2i}^{i}-C_{2i}^{i-1}

Hi=j=1iHj1HijH_i=\sum_{j=1}^{i}H_{j-1} H_{i-j}