基础数论指南 QwQ

数论

同余

同余是指:

  • a × b ≡ ( a   m o d   p ) × ( b   m o d   p ) ( m o d p ) a\times b\equiv (a\bmod p)\times(b\bmod p)\pmod p a×b(amodp)×(bmodp)(modp)
  • a + b ≡ ( a   m o d   p ) + ( b   m o d   p ) ( m o d p ) a+b\equiv (a\bmod p)+(b\bmod p)\pmod p a+b(amodp)+(bmodp)(modp)
  • a − b ≡ ( a   m o d   p ) − ( b   m o d   p ) ( m o d p ) a-b\equiv (a\bmod p)-(b\bmod p)\pmod p ab(amodp)(bmodp)(modp)

这个性质有助于我们推导数论公式和简化题目。比如有的题目要求答案对 1 0 9 + 7 10^9+7 109+7 取模,但是计算的过程中随时可能爆 long long,如果计算答案的过程中只包含 + + + − - × \times ×,就可以用同余来规避开高精度。

注意, ÷ \div ÷ 并不满足同余性质,如果涉及到 ÷ \div ÷ 的同余,那么就需要乘法逆元

欧拉函数

欧拉函数是 φ \varphi φ 函数,为 1 1 1 n n n 之内有多少个数与 n n n 互质。即 φ ( n ) = ∑ i = 1 n [ gcd ⁡ ( i , n ) = 1 ] \varphi(n)=\sum_{i=1}^n[\gcd(i,n)=1] φ(n)=i=1n[gcd(i,n)=1]

φ \varphi φ 具有如下性质。

  • 积性,即对于任意的 p , q ∈ P r i m e p,q\in Prime p,qPrime,都有 φ ( p ) × φ ( q ) = φ ( p × q ) \varphi(p)\times\varphi(q)=\varphi(p\times q) φ(p)×φ(q)=φ(p×q)
  • 有这个性质: ( ∑ d ∣ n φ ( d ) ) = n (\sum_{d\mid n}\varphi(d))=n (dnφ(d))=n
  • 欧拉定理,在后面会详解。

第一个性质可以用欧拉函数的求解方法来证明。 φ ( p ) = p − 1 \varphi(p)=p-1 φ(p)=p1 且仅当 p ∈ P r i m e p\in Prime pPrime。因为在 [ 1 , p ] [1,p] [1,p] 之间,只有 gcd ⁡ ( p , p ) = p \gcd(p,p)=p gcd(p,p)=p

如果有 n = p 2 , p ∈ P r i m e n=p^2,p\in Prime n=p2,pPrime,那么 φ ( n ) = p × ( p − 1 ) \varphi(n)=p\times(p-1) φ(n)=p×(p1)。因为有 p p p 个数 q q q gcd ⁡ ( n , q ) = p \gcd(n,q)=p gcd(n,q)=p,所以是 n − p n-p np p × ( p − 1 ) p\times(p-1) p×(p1)

如果有 n = p × q , p , q ∈ P r i m e n=p\times q,p,q\in Prime n=p×q,p,qPrime,则可以用容斥原理证明。在 [ 1 , n ] [1,n] [1,n] 中有 n p = q \frac{n}{p}=q pn=q p p p 的倍数, n q = p \frac{n}{q}=p qn=p q q q 的倍数,减去这些,发现有 n p × q = 1 \frac{n}{p\times q}=1 p×qn=1 个数要加上,即 p × q − p − q + 1 p\times q-p-q+1 p×qpq+1,就是 ( p − 1 ) × ( q − 1 ) (p-1)\times(q-1) (p1)×(q1),故得证。

再推得深入一些,如果 n = q × q × r , p , q , r ∈ P r i m e n=q\times q\times r,p,q,r\in Prime n=q×q×r,p,q,rPrime,那么就是 n − n q − n p − n r + n p × q + n p × r + n q × r − n p × q × r = n − p × q − p × r − q × r + p + q + r − 1 = ( p − 1 ) × ( q − 1 ) × ( r − 1 ) n-\frac{n}{q}-\frac{n}{p}-\frac{n}{r}+\frac{n}{p\times q}+\frac{n}{p\times r}+\frac{n}{q\times r}-\frac{n}{p\times q\times r}=n-p\times q-p\times r-q\times r+p+q+r-1=(p-1)\times(q-1)\times(r-1) nqnpnrn+p×qn+p×rn+q×rnp×q×rn=np×qp×rq×r+p+q+r1=(p1)×(q1)×(r1)。那么,我们可以推出公式,设唯一分解定理是: n = Π p i ∈ P r i m e m p i c i n=\Pi_{p_i\in Prime}^{m}p_i^{c_i} n=ΠpiPrimempici,那么则有 φ ( n ) = n × Π p i ∈ P r i m e m ( p i − 1 p i ) \varphi(n)=n\times\Pi_{p_i\in Prime}^{m}(\frac{p_i-1}{p_i}) φ(n)=n×ΠpiPrimem(pipi1),所以欧拉函数可以用欧拉筛法筛出来。

int phi[MAXN];
bool flag[MAXN];
vector<int> prim;
inline void prework(){
	flag[1]=true;
    phi[1]=1;
	for(int i=2;i<MAXN;++i){
		if(!flag[i]){
			prim.push_back(i);
			phi[i]=i-1;//p in Prime,则  phi(p)=p-1 
		}
		for(int j=0;j<prim.size()&&i*prim[j]<MAXN;++j){//欧拉筛法 
			flag[i*prim[j]]=true;
			if(i%prim[j]){
				phi[i*prim[j]]=phi[i]*(prim[j]-1);//计算公式 
			}else{
				phi[i*prim[j]]=phi[i]*prim[j];//不是重复的质数 
				break;
			}
		}
	} 
}

如何证明 ( ∑ d ∣ n φ ( d ) ) = n (\sum_{d\mid n}\varphi(d))=n (dnφ(d))=n

很明显, [ gcd ⁡ ( n , m ) = d ] = [ gcd ⁡ ( n d , m d ) = 1 ] [\gcd(n,m)=d]=[\gcd(\frac{n}{d},\frac{m}{d})=1] [gcd(n,m)=d]=[gcd(dn,dm)=1],设 f ( x ) = ∑ i = 1 n [ gcd ⁡ ( i , n ) = x ] f(x)=\sum_{i=1}^n[\gcd(i,n)=x] f(x)=i=1n[gcd(i,n)=x],很明显, n = ∑ d ∣ n f ( i ) n=\sum_{d\mid n}f(i) n=dnf(i)。那么 f ( x ) = ∑ i = 1 ⌊ n x ⌋ [ gcd ⁡ ( i , ⌊ n d ⌋ ) = 1 ] f(x)=\sum_{i=1}^{\lfloor\frac{n}{x}\rfloor}[\gcd(i,\lfloor\frac{n}{d}\rfloor)=1] f(x)=i=1xn[gcd(i,dn⌋)=1],那么就是 f ( x ) = φ ( ⌊ n x ⌋ ) f(x)=\varphi(\lfloor\frac{n}{x}\rfloor) f(x)=φ(⌊xn⌋),故得证。这个性质可以用在欧拉反演中。

欧拉反演

我们利用性质 2 2 2
n = ∑ d ∣ n φ ( d ) n=\sum_{d\mid n}\varphi(d) n=dnφ(d)
n n n 替换成 gcd ⁡ \gcd gcd
gcd ⁡ ( i , j ) = ∑ d ∣ gcd ⁡ ( i , j ) φ ( d ) \gcd(i,j)=\sum_{d\mid\gcd(i,j)}\varphi(d) gcd(i,j)=dgcd(i,j)φ(d)
换成两个 ∑ \sum
gcd ⁡ ( i , j ) = ∑ d ∣ i ∑ d ∣ j φ ( d ) \gcd(i,j)=\sum_{d\mid i}\sum_{d\mid j}\varphi(d) gcd(i,j)=didjφ(d)
∑ gcd ⁡ \sum\gcd gcd
∑ i = 1 n gcd ⁡ ( i , n ) = ∑ d ∣ n ∑ i = 1 n ∑ d ∣ i φ ( d ) \sum_{i=1}^n\gcd(i,n)=\sum_{d\mid n}\sum_{i=1}^n\sum_{d\mid i}\varphi(d) i=1ngcd(i,n)=dni=1ndiφ(d)
再演化一下:
∑ i = 1 n gcd ⁡ ( i , n ) = ∑ d ∣ n φ ( d ) × n d \sum_{i=1}^n\gcd(i,n)=\sum_{d\mid n}\varphi(d)\times\frac{n}{d} i=1ngcd(i,n)=dnφ(d)×dn
得出结论,这就是欧拉反演。

例题

就是求出 ∑ i = 1 n ∑ j = 1 n [ gcd ⁡ ( i , j ) = 1 ] \sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=1] i=1nj=1n[gcd(i,j)=1],不妨转化一下,变成 ( ∑ i = 1 n ∑ j = 1 i [ gcd ⁡ ( i , j ) = 1 ] ) × 2 − 1 (\sum_{i=1}^n\sum_{j=1}^i[\gcd(i,j)=1])\times 2-1 (i=1nj=1i[gcd(i,j)=1])×21,这就变成了 ( ∑ i = 1 n − 1 φ ( i ) ) × 2 + 1 (\sum_{i=1}^{n-1}\varphi(i))\times 2+1 (i=1n1φ(i))×2+1,求出 φ \varphi φ 的前缀和就可以了。

#include<bits/stdc++.h>
#define MAXN 40004
using namespace std;
typedef long long ll;
int phi[MAXN];
ll pre[MAXN];
bool flag[MAXN];
vector<int> prim;
inline void prework(){
	flag[1]=true;
	phi[1]=1;
	for(int i=2;i<MAXN;++i){
		if(!flag[i]){
			prim.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prim.size()&&i*prim[j]<MAXN;++j){
			flag[i*prim[j]]=true;
			if(i%prim[j]){
				phi[i*prim[j]]=phi[i]*(prim[j]-1);
			}else{
				phi[i*prim[j]]=phi[i]*prim[j];
				break;
			}
		}
	}
	for(int i=1;i<MAXN;++i){
		pre[i]=pre[i-1]+phi[i];
	}
}
int main(){
	prework();
	int n;
	scanf("%d",&n);
	printf("%lld",n==1?0ll:pre[n-1]<<1|1);
	return 0;
}

欧拉定理

欧拉定理是指如果 gcd ⁡ ( n , m ) = 1 \gcd(n,m)=1 gcd(n,m)=1,则有 n φ ( m ) ≡ 1 ( m o d m ) n^{\varphi(m)}\equiv 1\pmod m nφ(m)1(modm)

证明:构造一个集合 S = 1 , 2 , 3 … φ ( m ) S={1,2,3\dots\varphi(m)} S=1,2,3φ(m),那么则有 Π i = 1 φ ( m ) − 1 S i ≡ Π i = 1 φ ( m ) − 1 ( S i × n ) ( m o d m ) \Pi_{i=1}^{\varphi(m)-1}S_i\equiv\Pi_{i=1}^{\varphi(m)-1}(S_i\times n)\pmod m Πi=1φ(m)1SiΠi=1φ(m)1(Si×n)(modm),因为 gcd ⁡ ( φ ( m ) , S i ) = 1 \gcd(\varphi(m),S_i)=1 gcd(φ(m),Si)=1,所以只有 n n n 的影响。而 gcd ⁡ ( n , m ) = 1 \gcd(n,m)=1 gcd(n,m)=1,所以 n n n 也没有影响。然后,同时消掉 Π i = 1 φ ( m ) − 1 S i \Pi_{i=1}^{\varphi(m)-1}S_i Πi=1φ(m)1Si,则得 n φ ( m ) ≡ 1 ( m o d m ) n^{\varphi(m)}\equiv 1\pmod m nφ(m)1(modm)

乘法逆元

如果 a × b ≡ 1 ( m o d p ) a\times b\equiv 1\pmod p a×b1(modp),则称 a a a b b b p p p 意义下的逆元,记作 b − 1 b^{-1} b1。在模 p p p 意义下, a × 1 b ≡ a × b − 1 ( m o d p ) a\times\frac{1}{b}\equiv a\times b^{-1}\pmod p a×b1a×b1(modp) a a a 在模 p p p 意义下有逆元存在且仅当 gcd ⁡ ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1。求乘法逆元有很多种方法。

快速幂求逆元

根据欧拉定理:
a p − 1 ≡ a × b − 1 ( m o d p ) a^{p-1}\equiv a\times b^{-1}\pmod p ap1a×b1(modp)
同时除以 a a a
a p − 2 ≡ b − 1 ( m o d p ) a^{p-2}\equiv b^{-1}\pmod p ap2b1(modp)
所以 b − 1 = a p − 2 b^{-1}=a^{p-2} b1=ap2,可以使用快速幂求。当 gcd ⁡ ( a , p − 2 ) = 1 \gcd(a,p-2)=1 gcd(a,p2)=1 的时候,可以用欧拉定理优化。

线性求逆元

这是递推求逆元的方式。很显然, i n v 1 = 1 inv_1=1 inv1=1。因为在任意模数下都有 1 × 1 ≡ 1 ÷ 1 ( m o d p ) 1\times 1\equiv 1\div 1\pmod p 1×11÷1(modp)。对于其他情况,我们令 d i v = ⌊ p i ⌋ div=\lfloor\frac{p}{i}\rfloor div=ip m o d = p   m o d   i mod=p\bmod i mod=pmodi
d i v × i + m o d ≡ 0 ( m o d p ) div\times i+mod\equiv 0\pmod p div×i+mod0(modp)
两边同时乘以 i − 1 × m o d − 1 i^{-1}\times mod^{-1} i1×mod1,即 1 i × m o d \frac{1}{i\times mod} i×mod1
d i v × m o d − 1 + i − 1 ≡ 0 ( m o d p ) div\times mod^{-1}+i^{-1}\equiv 0\pmod p div×mod1+i10(modp)
同时减去 d i v × m o d − 1 div\times mod^{-1} div×mod1
i − 1 ≡ d i v × m o d − 1 ≡ ( m o d p ) i^{-1}\equiv div\times mod^{-1}\equiv\pmod p i1div×mod1(modp)
带入式子:
i − 1 ≡ − ⌊ p i ⌋ × ( p   m o d   i ) − 1 ( m o d p ) i^{-1}\equiv-\lfloor\frac{p}{i}\rfloor\times(p\bmod i)^{-1}\pmod p i1ip×(pmodi)1(modp)
可以知道 p   m o d   i < i p\bmod i<i pmodi<i,所以可以应用之前求过的逆元,即:
i n v i ≡ − ⌊ p i ⌋ × i n v p   m o d   i ( m o d p ) inv_i\equiv-\lfloor\frac{p}{i}\rfloor\times inv_{p\bmod i}\pmod p inviip×invpmodi(modp)

ll inv[MAXN];
inline void prework(int mod){//模数 
	inv[1]=1;//inv[1]=1 证明过 
	for(int i=2;i<MAXN;++i){//递推 
		inv[i]=((-(mod/i)*inv[mod%i])%mod+mod)%mod;//递推式 
	}
}

裴蜀定理

对于任意的整数 a , b a,b a,b x , y x,y x,y,满足 gcd ⁡ ( a , b ) ∣ a × x + b × y \gcd(a,b)\mid a\times x+b\times y gcd(a,b)a×x+b×y,且存在 x , y x,y x,y 满足 a × x + b × y = gcd ⁡ ( a , b ) a\times x+b\times y=\gcd(a,b) a×x+b×y=gcd(a,b)

对于第一点,一定满足 gcd ⁡ ( a , b ) ∣ a , b \gcd(a,b)\mid a,b gcd(a,b)a,b,所以对于整数 x , y x,y x,y 一定满足。

对于第二点,如果 a × b = 0 a\times b=0 a×b=0,则 gcd ⁡ ( a , b ) = max ⁡ ( a , b ) \gcd(a,b)=\max(a,b) gcd(a,b)=max(a,b) { x , y } = { 1 , 0 } \{x,y\}=\{1,0\} {x,y}={1,0} 一定满足。

如果 a × b ≠ 0 a\times b\not=0 a×b=0,则同时除以 − gcd ⁡ ( a , b ) -\gcd(a,b) gcd(a,b),得 ( − a ) × x + ( − b ) × y = 1 (-a)\times x+(-b)\times y=1 (a)×x+(b)×y=1。当 gcd ⁡ ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1 的时候,可以证明消掉 ( x − y ) × ( a − b ) (x-y)\times(a-b) (xy)×(ab),等于 ( a − b ) × ( x − y ) (a-b)\times(x-y) (ab)×(xy),这一定是可以等于 1 1 1 的。

这个定理对于 n n n 个数一定也是正确的。

扩展欧几里得算法

和裴蜀定理类似,写作 e x g c d exgcd exgcd,用于求裴蜀定理中的 x x x y y y

这可以模拟 gcd ⁡ \gcd gcd 的过程。首先, gcd ⁡ ( a , b ) = gcd ⁡ ( a , a   m o d   b ) \gcd(a,b)=\gcd(a,a\bmod b) gcd(a,b)=gcd(a,amodb),那么可以将 x x x y y y 往回代: a × x + ( a   m o d   b ) × y = gcd ⁡ ( a , b ) a\times x+(a\bmod b)\times y=\gcd(a,b) a×x+(amodb)×y=gcd(a,b)

由于 a   m o d   b = a − ⌊ a b ⌋ × b a\bmod b=a-\lfloor\frac{a}{b}\rfloor\times b amodb=aba×b,那么再代入式中: a × x + ( a − ⌊ a b ⌋ × b ) × y = gcd ⁡ ( a , b ) a\times x+(a-\lfloor\frac{a}{b}\rfloor\times b)\times y=\gcd(a,b) a×x+(aba×b)×y=gcd(a,b),演化一下:
a × ( x + y ) − ⌊ a b ⌋ × b × y = gcd ⁡ ( a , b ) a\times(x+y)-\lfloor\frac{a}{b}\rfloor\times b\times y=\gcd(a,b) a×(x+y)ba×b×y=gcd(a,b)

之后,就可以带入求 e x g c d exgcd exgcd 了。

void exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){
		x=1;
		y=0;
		return;//可行解 
	}
	exgcd(b,a%b);//带入 gcd 
	ll t=x;
	x=y;
	y=t-a/b*y;//带入式子 
}

这一个性质也可以帮助我们求逆元,但是过于复杂而且有同样复杂度 log ⁡ n \log n logn 的快速幂,所以这里不展开介绍。

组合数学

多重集问题

先从最基础的开始。比如给你 n n n 个数,要求从中选择 m m m 个数,分顺序,有多少种选择?很明显,这个是 Π i = m n i \Pi_{i=m}^n i Πi=mni 种选择方案,即 n ! ( n − m ) ! \frac{n!}{(n-m)!} (nm)!n!。我们称排列 A n m = n ! ( n − m ) ! A_n^m=\frac{n!}{(n-m)!} Anm=(nm)!n!

假如给你 n n n 个数,要求从中选择 m m m 个数,有多少种不同的选择方案?考虑化繁为简。首先,分顺序的是 A n m A_n^m Anm 种,然后要除以顺序所带来的价值,也就是 m m m 个数从中选择 m − 1 m-1 m1 个数,即 A m − 1 m = m ! A_{m-1}^m=m! Am1m=m!,那么得出 C n m = A n m A m 1 = n ! m ! × ( n − m ) ! C_n^m=\frac{A_n^m}{A_m^1}=\frac{n!}{m!\times(n-m)!} Cnm=Am1Anm=m!×(nm)!n!

那么假如有 k k k 个集合,每一个集合都有 c i c_i ci p i p_i pi,那么这 n n n 个集合的并集中选出 m m m 个数的个数。设 n = ∑ i = 1 k c i n=\sum_{i=1}^{k}c_i n=i=1kci,那么第一项肯定是 A n m A_n^m Anm。接下来,要依次按照上面的做法除以 A c i 1 A_{c_i}^1 Aci1,所以就是 A n m Π i = 1 k A c i 1 \frac{A_n^m}{\Pi_{i=1}^{k}A_{c_i}^1} Πi=1kAci1Anm

当然,还有一种题型叫做插板法,就是在 n n n 个元素内插入 m m m 个板子,将这 n n n 个元素分成 m + 1 m+1 m+1 个块,求方案数。

由于元素完全相同,所以可以直接得出答案为 C m n C_m^n Cmn。这是一个结论,和欧拉反演的结论一样,都是助于把题目转换为插板法的形式,进行更优秀的解答。

如何求出 C C C?有几种方法。

C C C 递推式

很明显,可以看出 C C C 就是杨辉三角,可以使用杨辉三角进行递推。

ll C[MAXN][MAXN];
inline void prework(){
	C[1][1]=1;
	for(int i=2;i<MAXN;++i){
		C[i][1]=C[i][i]=1;//两端为 1 
		for(int j=2;j<i;++j){
			C[i][j]=C[i-1][j-1]+C[i-1][j];//杨辉三角 
		}
	}
}
阶乘逆元递推式

可以直接套用公式,如果要取模并且可以使用逆元,那就可以用逆元。

ll inv[MAXN],frac[MAXN];//有时候是只能够现场求逆元和阶乘,因为逆元存不下
inline ll C(int n,int m){
	if(n<m){
		return 0;//特判 
	}
	return frac[n]*inv[frac[m]]%MOD*inv[frac[n-m]]%MOD;//公式 
}

鸽巢原理

鸽巢原理是指有 m m m 个鸽巢和 n n n 只鸽子,每一只鸽子要飞进鸽巢中,至少有 1 1 1 个鸽巢至少有 ⌈ n m ⌉ \lceil\frac{n}{m}\rceil mn 只鸽子。

考虑反证法。如果每一个鸽巢最多有 ⌊ n m ⌋ \lfloor\frac{n}{m}\rfloor mn 只鸽子,那么最多有 ⌊ n m ⌋ × m \lfloor\frac{n}{m}\rfloor\times m mn×m 只鸽子。但是这可能不是 n n n,多以矛盾。

容斥原理

容斥原理适用于求多个集合的集合并的大小用的。假如有 n n n 个集合 S i S_i Si,那么要求 ∣ S 1 ∪ S 2 ∪ S 3 … S n ∣ |S_1\cup S_2\cup S_3\dots S_n| S1S2S3Sn,首先可以把所有元素的大小加和,即 ∑ i = 1 n ∣ S i ∣ \sum_{i=1}^{n}|S_i| i=1nSi,可以得其中有元素重复了,那么减去一些元素的交集, ∣ S 1 ∩ S 2 ∣ + ∣ S 2 ∩ S 3 ∣ … ∣ S n ∩ S 1 ∣ |S1\cap S_2|+|S_2\cap S_3|\dots|S_n\cap S_1| S1S2+S2S3SnS1,则有一些元素多减了。再加上 ∣ S 1 ∩ S 2 ∩ S 3 ∣ … ∣ S n − 1 ∩ S n ∩ S 1 ∣ |S_1\cap S_2\cap S_3|\dots |S_{n-1}\cap S_n\cap S_1| S1S2S3Sn1SnS1,又发现有一些多加了,重复执行此操作直至发现 ∩ i = 1 n S i \cap_{i=1}^{n}S_i i=1nSi 多加或者多减。通过上述过程证明:
∣ ∪ i = 1 n S i ∣ = ∑ m = 1 n ( − 1 ) ( m − 1 ) × ( ∑ a i < a i + 1 ) ∣ ∩ i = 1 m S a i ∣ |\cup_{i=1}^{n}S_i|=\sum_{m=1}^{n}(-1)^{(m-1)}\times(\sum_{a_i<a_{i+1}})|\cap_{i=1}^{m}S_{a_i}| i=1nSi=m=1n(1)(m1)×(ai<ai+1)i=1mSai
其中,每一个 a i a_i ai 代表枚举顺序。

容斥原理的难点在于枚举顺序和确定 ∣ S i ∣ |S_i| Si。有的时候, ∣ S i ∣ |S_i| Si 为 dp 式。有的时候, ∣ S i ∣ |S_i| Si 为函数。容斥原理比 dp 更加难操作和推理,非常棘手。

例题

题目要求求出:
∑ a i = x y [ gcd ⁡ ( a i ) = x ] [ lcm ⁡ ( a i ) = y ] \sum_{a_i=x}^{y}[\gcd(a_i)=x][\operatorname{lcm}(a_i)=y] ai=xy[gcd(ai)=x][lcm(ai)=y]

不难发现,每一个数必须在 x x x y y y 之间,每一个数可以表示为 t × y x ( t ≤ x ) t\times\frac{y}{x}(t\le x) t×xy(tx)。不同的就是这一个 t t t

t = y x t=\frac{y}{x} t=xy,并且设 t = Π i = 1 k p i c i ( p i ∈ P r i m e ) t=\Pi_{i=1}^{k}p_i^{c_i}(p_i\in Prime) t=Πi=1kpici(piPrime),那么可以证明,要求 gcd ⁡ \gcd gcd x x x,那么至少要有一个数,其 c i c_i ci 0 0 0。要求 lcm ⁡ \operatorname{lcm} lcm y y y,那么至少要有一个数,其 c i c_i ci c i c_i ci。答案为 ( c i + 1 ) n (c_i+1)^n (ci+1)n

那么,发现是至少,因此要减去所有 c i c_i ci [ 1 , c i ] [1,c_i] [1,ci] 的情况, c i c_i ci [ 0 , c i − 1 ] [0,c_i-1] [0,ci1] 的情况同理,要减一次。

再发现 [ 1 , c i − 1 ] [1,c_i-1] [1,ci1] 被减了两次,那么加回来,得答案为 ( c i + 1 ) n − 2 × ( c i ) n + ( c i − 1 ) n (c_i+1)^n-2\times(c_i)^n+(c_i-1)^n (ci+1)n2×(ci)n+(ci1)n。之后对于 p i p_i pi 不同考虑乘起来即可。

#include<bits/stdc++.h>
#define MAXN 350000
#define MOD 998244353
using namespace std;
typedef long long ll;
int n,x,y,top,p[MAXN],c[MAXN];
bool flag[MAXN];
vector<int> prim;
inline void prework(){
	flag[1]=true;
	for(int i=2;i<MAXN;++i){
		if(!flag[i]){
			prim.push_back(i);
		}
		for(int j=0;j<prim.size()&&i*prim[j]<MAXN;++j){
			flag[i*prim[j]]=true;
			if(i%prim[j]==0){
				break;
			}
		}
	} 
}
inline bool check(int x){
	if(x<=1){
		return false;
	}
	for(int i=2;i*i<=x;++i){
		if(x%i==0){
			return false;
		}
	}
	return true;
}
inline ll power(ll x,ll y){
	ll res=1;
	while(y){
		if(y&1){
			(res*=x)%=MOD;
		}
		(x*=x)%=MOD;
		y>>=1;
	}
	return res;
}
inline void split(int x){
	top=0;
	for(int i=0;i<prim.size()&&x!=1;++i){
		if(x%prim[i]==0){
			p[++top]=prim[i];
			c[top]=0;
			while(x%prim[i]==0){
				x/=prim[i];
				++c[top];
			}
		}
	}
	if(check(x)){
		p[++top]=x;
		c[top]=1;
	}
}
int main(){
	prework();
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d %d %d",&x,&y,&n);
		int t=y/x;
		split(t);
		ll ans=1;
		for(int i=1;i<=top;++i){
			ll a=power(c[i]+1,n);
			ll b=power(c[i],n);
			ll d=power(c[i]-1,n);
			(ans*=((a-2ll*b+d)%MOD+MOD)%MOD)%=MOD;
		}
		printf("%lld\n",ans);
	} 
	return 0;
}

卢卡斯定理

卢卡斯定理是用于求 C C C 的。假如要求 C n m   m o d   p C_n^m\bmod p Cnmmodp,并且 p p p 为质数,那么则有 C n m ≡ C n   m o d   p m   m o d   p × C ⌊ n p ⌋ ⌊ m p ⌋ ( m o d p ) C_n^m\equiv C_{n\bmod p}^{m\bmod p}\times C_{\lfloor\frac{n}{p}\rfloor}^{\lfloor\frac{m}{p}\rfloor}\pmod p CnmCnmodpmmodp×Cpnpm(modp)。我们发现,如果 n n n m m m 非常大的时候,可以用卢卡斯定理缩小 n n n m m m,右边的除法式还可以继续用卢卡斯,时间复杂度为 log ⁡ p n \log_p n logpn

证明右转 OI Wiki我不会告诉你我不会证明

扩展卢卡斯定理

这是在 p p p 不是质数的情况下使用的。主要思想是将 p p p 分解质因数,分别 l u c a s lucas lucas,然后用中国剩余定理合并答案。

例题

很明显,题目要求的是具有小根堆性质的数列。设 d p i dp_i dpi i i i 个数的排列中满足小根堆性质的个数。设 i i i 为根节点,那么有 i − 1 i-1 i1 个点是子节点。那么从 i − 1 i-1 i1 个节点中选择 l l l 个节点为左子节点,为 C i − 1 l C_{i-1}^l Ci1l,那么得出 d p i = C s o n i − 1 s o n ⌊ i 2 ⌋ × d p i × 2 × d p i × 2 + 1 dp_i=C_{son_i-1}^{son_{\lfloor\frac{i}{2}\rfloor}}\times dp_{i\times 2}\times dp_{i\times 2+1} dpi=Csoni1son2i×dpi×2×dpi×2+1。其中, s o n i son_i soni 表示在二叉树下 i i i 的子节点个数。

#include<bits/stdc++.h>
#define MAXN 2000002
using namespace std;
typedef long long ll;
int n,p,son[MAXN];
ll frac[MAXN],pre[MAXN],dp[MAXN];
inline void prework(){
	frac[0]=1;
	for(int i=1;i<MAXN;++i){
		dp[i]=1;
		frac[i]=frac[i-1]*i%p;
	}
	for(int i=n;i>=2;--i){
		++son[i];
		son[i>>1]+=son[i];
	}
	++son[1];
}
inline ll power(ll x,ll y){
	ll res=1;
	while(y){
		if(y&1){
			(res*=x)%=p;
		}
		(x*=x)%=p;
		y>>=1;
	}
	return res;
}
ll C(int n,int m){
	if(m>n){
		return 0;
	}
	return frac[n]*power(frac[m],p-2)%p*power(frac[n-m],p-2)%p;
}
ll lucas(int n,int m){
	if(!m){
		return 1;
	}
	return C(n%p,m%p)*lucas(n/p,m/p)%p;
}
int main(){
    scanf("%d %d",&n,&p);
 	prework();
   	for(int i=n;i>=1;--i){
    	dp[i]=lucas(son[i]-1,son[i<<1])*dp[i<<1]%p*dp[i<<1|1]%p;
	}
    printf("%lld",dp[1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值