数论小结

前置

快速幂|快速乘

问题1.求 a b % p a^b\%p ab%p的值
问题2.求 a ∗ b % p a*b\%p ab%p的值

这个不解释了,直接上代码
问题1:快速幂 O ( l o g n ) O(logn) O(logn)

long long kasumi(long long a,long long b,long long p)
{
	long long ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}

问题2:快速乘 O ( l o g n ) O(logn) O(logn)

long long kasumi(long long a,long long b,long long p)
{
	long long ans=0;
	while(b)
	{
		if(b&1) ans=ans+a%p;
		a=(a+a)%p;
		b>>=1;
	}
	return ans;
}

问题2:快速乘 O ( 1 ) O(1) O(1)

long long mul(long long a,long long b,long long mod)
{
	long long ans=a*b-(long long)((long double) a*b/mod+0.5)*mod;
	return ans>=0?ans:ans+mod;
}

筛法

埃拉托色尼筛

用o(nloglogn)的时间输出小于n的所有某积性函数

此处以素数为例,找到一个素数,筛去他的所有倍数,复杂度为nloglogn,调和级数再乘以质数密度

	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			printf("%d ",i);
		}
		for(int j=i;j<=n;j+=i)
		{
			vis[j]=1;
		}
	}

线性筛素数

用o(n)的时间输出小于n的所有素数

如果每个数都被他自己最小的质因子筛掉,显然一个数只会被访问一次
按照此思路即可构建线性筛

代码在i枚举每一个p[j]直至i是p[j]的倍数,这说明接下去的i*p[k]都能被这个p[j]乘上一个更大的i筛掉,为了保证每个数都被最小质因子筛掉,这时候break出去

	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			p[++cnt]=i;
			vis[i]=1;
		}
		for(int j=1;j<=cnt;j++)
		{
			if(p[j]*i>n) break;
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}

线性筛欧拉函数

用o(n)的时间输出小于等于n的所有 ϕ ( x ) \phi(x) ϕ(x)

欧拉函数

欧拉函数 ϕ ( x ) \phi(x) ϕ(x)表示1到x中与x互质的数的个数
很显然可以得出一个最直观的公式
ϕ ( x ) = x ∏ i = 1 n ( 1 − 1 p i ) \phi(x)=x\prod_{i=1}^{n}(1-\frac{1}{p_i}) ϕ(x)=xi=1n(1pi1)
其中 p i p_i pi是所有n的质因子

线性筛

根据欧拉函数的这个性质,我们还是按照线性筛去思考
现在已知 p [ j ] , i , ϕ ( i ) p[j],i,\phi(i) p[j],i,ϕ(i)
显然 ϕ ( i ∗ p [ j ] ) = ϕ ( i ) ∗ p [ j ] ∗ ( 1 − 1 p [ j ] ) = ϕ ( i ) ∗ ( p [ j ] − 1 ) \phi(i*p[j])=\phi(i)*p[j]*(1-\frac{1}{p[j]})=\phi(i)*(p[j]-1) ϕ(ip[j])=ϕ(i)p[j](1p[j]1)=ϕ(i)(p[j]1)
但当 i % p [ j ] = = 0 i\%p[j]==0 i%p[j]==0的时候很显然p[j]已经是i的因子了,所以 ϕ ( i ∗ p [ j ] ) = ϕ ( i ) ∗ p [ j ] \phi(i*p[j])=\phi(i)*p[j] ϕ(ip[j])=ϕ(i)p[j]
最后别忘了, ϕ ( p [ j ] ) = p [ j ] − 1 \phi(p[j])=p[j]-1 ϕ(p[j])=p[j]1

phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			vis[i]=1;
			p[++cnt]=i;
			phi[i]=i-1; 
		}
		for(int j=1;j<=cnt;j++)
		{
			if(i*p[j]>n) break;
			vis[i*p[j]]=1;
			if(i%p[j])
			{
				phi[i*p[j]]=phi[i]*(p[j]-1);
			}
			else
			{
				phi[i*p[j]]=phi[i]*p[j];
				break;
			} 
		}
	}

线性筛莫比乌斯函数

用o(n)的时间输出小于n的所有 μ ( x ) \mu(x) μ(x)

莫比乌斯函数

莫比乌斯函数定义如下
在这里插入图片描述
具体用法到莫比乌斯反演部分再说

线性筛

我们只考虑用同样的方法如何筛一个莫比乌斯函数
首先是p[j],他只有一个质因子,所以 μ ( p [ j ] ) = − 1 \mu(p[j])=-1 μ(p[j])=1
然后我们又已知了 p [ j ] , μ ( i ) , i p[j],\mu(i),i p[j],μ(i),i
还是两种情况,普通就是多加进来一个质数 μ ( p [ j ] ∗ i ) = − μ ( i ) \mu(p[j]*i)=-\mu(i) μ(p[j]i)=μ(i)
而能整除时,显然引入了一个平方数因子,故 μ ( p [ j ] ∗ i ) = 0 \mu(p[j]*i)=0 μ(p[j]i)=0

	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			mu[i]=-1;
			p[++cnt]=i;
			vis[i]=1;
		}
		for(int j=1;j<=cnt;j++)
		{
			if(i*p[j]>n) break;
			vis[i*p[j]]=1;
			if(i%p[j])
			{
				mu[i*p[j]]=-mu[i];
			}
			else
			{
				mu[i*p[j]]=0;
				break;
			}
		}
	}

一、欧几里得、扩展欧几里得、类欧几里得

欧几里得

( a , b ) (a,b) (a,b)表示 a a a b b b的最大公约数,求 ( a , b ) (a,b) (a,b)

c = ( a , b ) c=(a,b) c=(a,b),则 c ∣ a c|a ca c ∣ b c|b cb
r = a − b r=a-b r=ab,则 r = a − b = k 1 c − k 2 c = ( k 1 − k 2 ) c r=a-b=k_1c-k_2c=(k_1-k_2)c r=ab=k1ck2c=(k1k2)c
可以发现 r r r c c c的倍数,同样的,对于 r = a − b ∗ k r=a-b*k r=abk上面的性质一样满足
k = [ a b ] k=[\frac{a}{b}] k=[ba],则 r = a % b r=a\%b r=a%b,同样 ( a , b ) = ( a % b , b ) (a,b)=(a\%b,b) (a,b)=(a%b,b)
由此可以递归求解 ( a , b ) (a,b) (a,b)

代码: O ( l o g n ) O(logn) O(logn)

long long gcd(long long a,long long b)
{
	if(!b) return a;
	return gcd(b,a%b);
}

扩展欧几里得

求解不定方程 a x + b y = c ax+by=c ax+by=c(x,y均为整数)

贝祖定理:若 a x + b y = c ax+by=c ax+by=c,则 ( a , b ) ∣ c (a,b)|c (a,b)c
证明:
因为 ( a , b ) ∣ a , ( a , b ) ∣ b (a,b)|a,(a,b)|b (a,b)a(a,b)b,所以 ( a , b ) ∣ ( a x + b y ) (a,b)|(ax+by) (a,b)(ax+by),所以 ( a , b ) ∣ c (a,b)|c (a,b)c
a n s ans ans a x + b y = c ax+by=c ax+by=c的最小正元素即 ∀ c   a n s ∣ c \forall c \ ans|c c ansc
d = [ a a n s ] d=[\frac{a}{ans}] d=[ansa] r = a % a n s = a − d ∗ ( a x + b y ) = a ( 1 − d x ) + b ∗ ( − d y ) r=a\%ans=a-d*(ax+by)=a(1-dx)+b*(-dy) r=a%ans=ad(ax+by)=a(1dx)+b(dy)
显然 r r r仍为 a , b a,b a,b的线性组合
所以以此类推 s ∣ a , s ∣ b s|a,s|b sa,sb
由此得 ( a , b ) ∣ s (a,b)|s (a,b)s
( a , b ) ∣ a , ( a , b ) ∣ b (a,b)|a,(a,b)|b (a,b)a,(a,b)b
所以 s ∣ ( a , b ) s|(a,b) s(a,b)
综上 s = ( a , b ) s=(a,b) s=(a,b)
仅当 c c c ( a , b ) (a,b) (a,b)的整数倍是该方程有解

以上的公式告诉我们,只需要解 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)即可。

假设我们现在要解
b ∗ x 1 + a % b ∗ y 1 = g c d ( b , a % b ) b*x1+a\%b*y1=gcd(b,a\%b) bx1+a%by1=gcd(b,a%b)
其中 a % b = a − [ a b ] ∗ b a\%b=a-[\frac{a}{b}]*b a%b=a[ba]b
代入得
b ∗ x 1 + ( a − [ a b ] ∗ b ) ∗ y 1 = g c d ( b , a % b ) b*x1+(a-[\frac{a}{b}]*b)*y1=gcd(b,a\%b) bx1+(a[ba]b)y1=gcd(b,a%b)
移个项,把a,b分开
a ∗ y 1 + b ∗ ( x 1 − [ a b ] ∗ y 1 ) = g c d ( b , a % b ) a*y1+b*(x1-[\frac{a}{b}]*y1)=gcd(b,a\%b) ay1+b(x1[ba]y1)=gcd(b,a%b)
根据辗转相除法的定义, g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
所以这个式子和
a ∗ x + b ∗ y = g c d ( a , b ) 完 全 等 价 a*x+b*y=gcd(a,b)完全等价 ax+by=gcd(a,b)
其中x=y1,y=(x1-a/b*y1)
于是可以在求解gcd的过程中求解这个方程

int exgcd(int a,int b,int &x,int &y)
{
	if(!b)
	{
		x=1,y=0;
		return a;
	}
	int tmp2=exgcd(b,a%b,x,y);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return tmp2;
} 

类欧几里得

求解 ∑ i = 0 n [ a ∗ i + b c ] \sum_{i=0}^{n}[\frac{a*i+b}{c}] i=0n[cai+b]

类欧是近几年才火热起来的算法,新颖多变,这里以这个模板问题求解

注意这个跟欧几里得思路不同,请清空大脑

首先对于大于c的a和b,我们可以把它们变成a%c,b%c,拿出来的 [ a c ] ∗ i 和 [ b c ] [\frac{a}{c}]*i和[\frac{b}{c}] [ca]i[cb]对答案的贡献分别为 [ a c ] ∗ n ∗ ( n + 1 ) 2 [\frac{a}{c}]*\frac{n*(n+1)}{2} [ca]2n(n+1) [ b c ] ∗ ( n + 1 ) [\frac{b}{c}]*(n+1) [cb](n+1)

此时我们已经使新的a,b都满足a<c,b<c
∑ i = 0 n [ a ∗ i + b c ] \sum_{i=0}^{n}[\frac{a*i+b}{c}] i=0n[cai+b]的定义可以理解为一条一次函数与x轴,y轴,y=n轴围成的图形中的点的个数
所以枚举这些点
点数= ∑ i = 0 n ∑ j = 0 m [ j < = [ a ∗ i + b c ] ] \sum_{i=0}^{n}\sum_{j=0}^{m}[j<=[\frac{a*i+b}{c}]] i=0nj=0m[j<=[cai+b]]
显然两边同乘以c照样成立
点数= ∑ i = 0 n ∑ j = 1 m [ j ∗ c < = [ a ∗ i + b ] ] \sum_{i=0}^{n}\sum_{j=1}^{m}[j*c<=[a*i+b]] i=0nj=1m[jc<=[ai+b]]
大于等于的括号也可以去,去完移个项
点数= ∑ i = 0 n ∑ j = 1 m [ j ∗ c − b < = a ∗ i ] \sum_{i=0}^{n}\sum_{j=1}^{m}[j*c-b<=a*i] i=0nj=1m[jcb<=ai]
把j用j+1替换
点数= ∑ i = 0 n ∑ j = 0 m − 1 [ j ∗ c + c − b < = a ∗ i ] \sum_{i=0}^{n}\sum_{j=0}^{m-1}[j*c+c-b<=a*i] i=0nj=0m1[jc+cb<=ai]
减个一把等号去掉
点数= ∑ i = 0 n ∑ j = 0 m − 1 [ j ∗ c + c − b − 1 < a ∗ i ] \sum_{i=0}^{n}\sum_{j=0}^{m-1}[j*c+c-b-1<a*i] i=0nj=0m1[jc+cb1<ai]
把a移到左边去
点数= ∑ i = 0 n ∑ j = 0 m − 1 [ ( j ∗ c + c − b − 1 ) a < i ] \sum_{i=0}^{n}\sum_{j=0}^{m-1}[\frac{(j*c+c-b-1)}{a}<i] i=0nj=0m1[a(jc+cb1)<i]
大于这个的i有几个呢?自然是 n − [ ( j ∗ c + c − b − 1 ) a ] n-[\frac{(j*c+c-b-1)}{a}] n[a(jc+cb1)]
于是可以把外面这个求和拆掉
点数= ∑ j = 0 m − 1 n − [ ( j ∗ c + c − b − 1 ) a ] \sum_{j=0}^{m-1}n-[\frac{(j*c+c-b-1)}{a}] j=0m1n[a(jc+cb1)]
可以继续拆
点数= n ∗ m − ∑ j = 0 m − 1 [ ( c ∗ j + c − b − 1 ) a ] n*m-\sum_{j=0}^{m-1}[\frac{(c*j+c-b-1)}{a}] nmj=0m1[a(cj+cb1)]
可以发现右边这个求和式与上面所求的式子格式一致
于是可以类似欧几里得递归求解
这也就是类欧名字的来源

inline int lgcd(int a,int b,int c,int n)
{
	if(n<0) return 0;
	if(!a) return b/c*(n+1);
	int ans=0;
	ans+=(a/c)*n*(n+1)/2+(b/c)*(n+1);
	a=a%c,b=b%c;
	int m=(a*n+b)/c;
	ans+=n*m-lgcd(c,c-b-1,a,m-1);
	return ans;
}

二、欧拉定理、费马小定理、扩展欧拉定理

a n % p a^n\%p an%p(n>1e100)

欧拉定理

定义欧拉函数 ϕ ( x ) \phi(x) ϕ(x),表示在 1 − n 1-n 1n中与 n n n互质的数的个数
在所有与模数 m m m互质的剩余类中各取出一个代表元,构成一个缩剩余系,则该缩系的大小为 ϕ ( m ) \phi(m) ϕ(m)
记该缩系中的所有元素为 a 1 , a 2 , a 3 . . . a ϕ ( m ) a_1,a_2,a_3...a_{\phi(m)} a1,a2,a3...aϕ(m)
如果 ( k , m ) = 1 (k,m)=1 (k,m)=1
k a 1 , k a 2 , k a 3 . . . k a ϕ ( m ) ka_1,ka_2,ka_3...ka_{\phi(m)} ka1,ka2,ka3...kaϕ(m)仍为一个缩系
所以 ∏ i = 1 ϕ ( m ) k a i ≡ ∏ i = 1 ϕ ( m ) a i \prod_{i=1}^{\phi(m)}ka_i\equiv\prod_{i=1}^{\phi(m)}ai i=1ϕ(m)kaii=1ϕ(m)ai
k ϕ ( m ) ≡ 1 ( m o d m ) k^{\phi(m)}\equiv1(modm) kϕ(m)1(modm)
证毕

这两年前的证明好学术化……
搞个通俗易懂的

首先k与p互质
考虑小于p的这些与其互质的数为 a 1 , a 2 . . . . . . a n a_1,a_2......a_n a1,a2......an
p 1 = a 1 k , p 2 = a 2 k . . . . . . p n = a n k p_1=a_1k,p_2=a_2k......p_n=a_nk p1=a1k,p2=a2k......pn=ank
首先第一条引理
p 1 , p 2 , p 3 . . . . . . p n p_1,p_2,p_3......p_n p1,p2,p3......pn在%p下余数均不相同
这个很好证
假设 p 1 p_1 p1 p 2 p_2 p2余数相同
p 1 − p 2 = i ∗ p p_1-p_2=i*p p1p2=ip
代入得 k ∗ ( a 1 − a 2 ) = i ∗ p k*(a_1-a_2)=i*p k(a1a2)=ip
首先k不是p的倍数,其次 a 1 , a 2 a_1,a_2 a1,a2余数不相同,所以不成立
所以引理一成立

然后第二条引理
p n p_n pn仍与p互质
这个也是显然的嘛
因为k与p互质, a n a_n an与p互质, k ∗ a n k*a_n kan自然与p互质

现在注意到 p n p_n pn有n个,两两余数不同,又与p互质
可以感受到这些 p n p_n pn a n a_n an是以某种关系一一对应的,
那么 ∏ i = 1 ϕ ( p ) p i = ∏ i = 1 ϕ ( p ) a i \prod_{i=1}^{\phi(p)}p_i=\prod_{i=1}^{\phi(p)}a_i i=1ϕ(p)pi=i=1ϕ(p)ai
∏ i = 1 ϕ ( p ) k ∗ a i = ∏ i = 1 ϕ ( p ) a i \prod_{i=1}^{\phi(p)}k*a_i=\prod_{i=1}^{\phi(p)}a_i i=1ϕ(p)kai=i=1ϕ(p)ai
k ϕ ( p ) ∗ ∏ i = 1 ϕ ( p ) a i = ∏ i = 1 ϕ ( p ) a i k^{\phi(p)}*\prod_{i=1}^{\phi(p)}a_i=\prod_{i=1}^{\phi(p)}a_i kϕ(p)i=1ϕ(p)ai=i=1ϕ(p)ai
k ϕ ( p ) ≡ 1 ( m o d   p ) k^{\phi(p)}\equiv1(mod \ p) kϕ(p)1(mod p)

这个定理有什么用呢?
a n % p a^n\%p an%p满足 g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1时, a n % p = a n % ϕ ( p ) % p a^n\%p=a^{n\%\phi(p)}\%p an%p=an%ϕ(p)%p
幂就降下来了

费马小定理

ϕ ( p ) = p − 1 \phi(p)=p-1 ϕ(p)=p1带入欧拉定理既得
k p − 1 ≡ 1 ( m o d p ) k^{p-1}\equiv1(modp) kp11(modp)

扩展欧拉定理

那么如果 g c d ( a , p ) ! = 1 gcd(a,p)!=1 gcd(a,p)!=1我们刚才的一些假设就会gg
现在引入扩展欧拉定理补充不互质的情况
公式如下
在这里插入图片描述

第一条就是我们得到的普通欧拉定理
下面的现在证明
这个是一个构造,与欧拉定理证明不一样,请及时更换思路
假设有质数p,模数为m
p c ≡ p c % p h i ( m ) + ϕ ( m ) p^c\equiv p^{c\%phi(m)+\phi(m)} pcpc%phi(m)+ϕ(m)这个相当于降幂以后再乘个1,显然成立
然后推广到幂指数上
( p k ) c = p k c = p k c + k ϕ ( m ) = ( p k ) c % p h i ( m ) + p h i ( m ) (p^k)^c=p^{kc}=p^{kc+k\phi(m)}=(p^k)^{c\%phi(m)+phi(m)} (pk)c=pkc=pkc+kϕ(m)=(pk)c%phi(m)+phi(m)反正对于p来说随便补 ϕ ( m ) \phi(m) ϕ(m)
然后推广到任意自然数a
显然对于a的每个质因子的幂次这个公式都满足,所以对于a该式也成立
a c = a c % p h i ( m ) + p h i ( m ) a^c=a^{c\%phi(m)+phi(m)} ac=ac%phi(m)+phi(m)
就是先构造了一个,然后证明他成立,至于这个构造是怎么来的……しらない

三、根据上述知识处理逆元

记a逆元b满足 a ∗ b ≡ 1 ( m o d   p ) a*b\equiv1(mod\ p) ab1(mod p)

如果gcd(a,p)不为一,就不存在逆元!

p是质数

根据费马小定理, a p − 1 ≡ 1 ( m o d   p ) a^{p-1}\equiv1(mod\ p) ap11(mod p),且 a ∗ b ≡ 1 ( m o d   p ) a*b\equiv1(mod\ p) ab1(mod p),于是可以推出 b ≡ a p − 2 ( m o d   p ) b\equiv a^{p-2}(mod\ p) bap2(mod p)

int inv=kasumi(a,mod-2);

p是合数

我们发现 a ∗ b ≡ 1 ( m o d   p ) a*b\equiv1(mod \ p) ab1(mod p)
则很显然 a ∗ b − p ∗ k = 1 a*b-p*k=1 abpk=1
这是一个不定方程,可以用exgcd求解
负数并不要紧,我们关心的只是b

exgcd(a,p,x,y);
x=(x%p+p)%p;

线性筛逆元

现在我们要求i的逆元
p = k ∗ i + r ( r < i ) p=k*i+r(r<i) p=ki+r(r<i)
两边同乘以 r − 1 ∗ i − 1 r^{-1}*i^{-1} r1i1
可以得到 p ∗ r − 1 ∗ i − 1 = k ∗ r − 1 + i − 1 p*r^{-1}*i^{-1}=k*r^{-1}+i^{-1} pr1i1=kr1+i1
两边对p取模,移项
i − 1 = − k ∗ r − 1 i^{-1}=-k*r^{-1} i1=kr1
k = [ p i ] k=[\frac{p}{i}] k=[ip]

inv[i]=(p-p/i)*inv[p%i]%p;

四、中国剩余定理、扩展中国剩余定理

x%3=a,x%5=b,x%7=c,求x

中国剩余定理

中国剩余定理的实质又是一个构造
假设模数是 p 1 , p 2 , p 3 , p 4 . . . . . . p n p_1,p_2,p_3,p_4......p_n p1,p2,p3,p4......pn且这些模数两两互质
令A=lcm ( p 1 , p 2 , p 3 . . . . . . , p n ) (p_1,p_2,p_3......,p_n) (p1,p2,p3......,pn)
A i = A p i A_i=\frac{A}{p_i} Ai=piA
B i ≡ A i − 1 ( m o d   p i ) B_i\equiv A_i^{-1}(mod\ p_i) BiAi1(mod pi)
x ≡ ∑ i = 1 n x i A i B i ( m o d A ) x\equiv \sum_{i=1}^{n}x_iA_iB_i(mod A) xi=1nxiAiBi(modA)

现在证明此类构造的合理性
对于任意的 p i p_i pi因为其他的 A j A_j Aj都是 p i p_i pi的倍数,所以 x ≡ x i A i B i ( m o d   p i ) x\equiv x_iA_iB_i(mod \ p_i) xxiAiBi(mod pi)
然后根据上面构造的
A i B i ≡ 1 ( m o d   p i ) A_iB_i\equiv 1(mod \ p_i) AiBi1(mod pi)
所以对于每一个这个x都成立
所以构造合理

	for(int i=1;i<=n;i++) A=A*a[i];
	for(int i=1;i<=n;i++)
	{
		int tmp=A/a[i];
		int x,y;
		exgcd(tmp%a[i],a[i],x,y);
		inv[i]=(x%a[i]+a[i])%a[i];
		ans=(ans+b[i]*tmp%A*inv[i]%A)%A;
	}

扩展中国剩余定理

这个与CRT有一些区别,请先放掉CRT的构造思维

我们现在有若干个条件
x ≡ a 1 ( m o d   b 1 ) x \equiv a_1(mod\ b_1) xa1(mod b1)
x ≡ a 2 ( m o d   b 2 ) x \equiv a_2(mod\ b_2) xa2(mod b2)
x ≡ a 3 ( m o d   b 3 ) x \equiv a_3(mod\ b_3) xa3(mod b3)

考虑合并两个条件
x ≡ k ( m o d   l c m ( b 1 , b 2 ) ) x \equiv k(mod \ lcm(b_1,b_2)) xk(mod lcm(b1,b2))
x = a 1 + k 1 ∗ b 1 = a 2 + k 2 ∗ b 2 x=a_1+k_1*b_1=a_2+k_2*b_2 x=a1+k1b1=a2+k2b2
k 1 b 1 − k 2 b 2 = a 2 − a 1 k_1b_1-k_2b_2=a_2-a_1 k1b1k2b2=a2a1
显然这是个不定方程,又可以用exgcd求解,存在解的条件是 g c d ( b 1 , b 2 ) ∣ ( a 2 − a 1 ) gcd(b_1,b_2)|(a_2-a_1) gcd(b1,b2)(a2a1)
有解的话就解出 k 1 , k 2 k_1,k_2 k1,k2合并x即可
其中 k 1 = a 2 − a 1 g c d ( b 1 , b 2 ) k 1 , k 2 = − a 2 − a 1 g c d ( b 1 , b 2 ) k 2 k_1=\frac{a_2-a_1}{gcd(b_1,b_2)}k_1,k_2=-\frac{a_2-a_1}{gcd(b_1,b_2)}k_2 k1=gcd(b1,b2)a2a1k1,k2=gcd(b1,b2)a2a1k2用其中任意一个还原即可

long long excrt(long long w1,long long p1,long long w2,long long p2,long long &w,long long &p)
{
	long long gg=w2-w1;
	long long g=exgcd(p1,p2,x,y);
	if(gg%g) return 0;
	long long tmp=gg/g;
	x=mul(x,tmp,p2/g);
	p=p1/g*p2;
	w=(w1+mul(x,p1,p)%p+p)%p;
	return 1;
}

五、组合数取模、卢卡斯、扩展卢卡斯

C n m % p C_n^m\%p Cnm%p

最简单的组合数取模

long long get_c(long long x,long long y)
{
	return fac[x]*inv[x-y]%mod*inv[y]%mod;
}

fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=kasumi(fac[n],mod-2);
for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;

卢卡斯定理

( n m ) % p = ( [ n / p ] [ m / p ] ) ∗ ( n % p m % p ) (_{n}^{m})\%p=(_{[n/p]}^{[m/p]})*(_{n\%p}^{m\%p}) (nm)%p=([n/p][m/p])(n%pm%p)
显然左边的这个除式可以递推,复杂度在去掉预处理p之后是log级的
现在证明
首先 ( 1 + x ) p ≡ ( 1 + x p ) ( m o d   p ) (1+x)^p\equiv (1+x^p)(mod\ p) (1+x)p(1+xp)(mod p)
这是显然的,因为展开以后,除了开头结尾这两项,剩下带的组合数都是p的倍数
然后 ( 1 + x ) m = ( 1 + x ) a 1 ∗ ( ( 1 + x ) p ) a 2 ∗ . . . ∗ ( ( 1 + x ) p k ) a k (1+x)^m=(1+x)^{a_1}*((1+x)^p)^{a_2}*...*((1+x)^{p^k})^{a_k} (1+x)m=(1+x)a1((1+x)p)a2...((1+x)pk)ak
相当于把m拆成p进制
根据之前那个式子,可以拆括号
( 1 + x ) m ≡ ( 1 + x ) a 1 ∗ ( 1 + x p ) a 2 ∗ . . . ∗ ( 1 + x p k ) a k (1+x)^m\equiv (1+x)^{a_1}*(1+x^p)^{a_2}*...*(1+x^{p^k})^{a_k} (1+x)m(1+x)a1(1+xp)a2...(1+xpk)ak
然后 ( n m ) (_n^m) (nm)就相当于里面的 x n x^n xn的项数
同样把n拆成p进制
n = b 1 + p ∗ b 2 + p 2 ∗ b 3 + . . . . . . + p k ∗ b k n=b_1+p*b_2+p^2*b_3+......+p^k*b_k n=b1+pb2+p2b3+......+pkbk
那么问题就变成了分别在每个堆 a i a_i ai里取 b i b_i bi
这种表示是唯一的,因为进制转换是唯一的
于是答案转化成了 ∏ ( b i a i ) \prod(_{b_i}^{a_i}) (biai)
递归式就是上式

long long c(long long n,long long m)
{
	return fac[m]*inv[m-n]%p*inv[n]%p;
}

long long lucas(long long n,long long m)
{
	if(n<p&&m<p) return c(n,m);
	return c(n%p,m%p)*lucas(n/p,m/p)%p;
}

扩展卢卡斯定理

刚才的情况默认p是质数
现在p变成合数了,于是又有了扩展卢卡斯

这个思路又和卢卡斯不大一样,请及时更换思路

首先我们把这个合数拆成若干个质因子的幂次之积,然后分别用卢卡斯求解,再用CRT合并,这是大体思路
那么先考虑 ( n m ) % p i (_n^m)\%p^i (nm)%pi
( n m ) % p i = m ! n ! ∗ ( m − n ) ! % p i (_n^m)\%p^i=\frac{m!}{n!*(m-n)!}\%p^i (nm)%pi=n!(mn)!m!%pi
然后这里无法保证m!与 p i p^i pi互质
所以使用一些操作把m!里的p都拿出来,就可以对这些模 p i p^i pi
m ! p a n ! p b ∗ ( m − n ) ! p c ∗ p a − b − c   % p i \frac{\frac{m!}{p^a}}{\frac{n!}{p^b}*\frac{(m-n)!}{p^c}}*p^{a-b-c}\ \%p^i pbn!pc(mn)!pam!pabc %pi
于是问题转化成 m ! p a % p i \frac{m!}{p^a}\%p^i pam!%pi
对于这个
还是先看m!
为了加深理解,手模一下
假设m=22,p=3,i=2
22 ! = 1 ∗ 2 ∗ 3 ∗ 4 ∗ 5 ∗ 6 ∗ 7 ∗ 8 ∗ 9 ∗ 10 ∗ 11 ∗ 12 ∗ 13 ∗ 14 ∗ 15 ∗ 16 ∗ 17 ∗ 18 ∗ 19 ∗ 20 ∗ 21 ∗ 22 22!=1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20*21*22 22!=12345678910111213141516171819202122
把3提取出来
22 ! = 3 7 ∗ ( 7 ! ) ∗ ( 1 ∗ 2 ∗ 4 ∗ 5 ∗ 7 ∗ 8 ∗ 10 ∗ 11 ∗ 13 ∗ 14 ∗ 16 ∗ 17 ∗ 19 ∗ 20 ∗ 22 ) 22!=3^7*(7!)*(1*2*4*5*7*8*10*11*13*14*16*17*19*20*22) 22!=37(7!)(124578101113141617192022)
其中7!可以递归求解, 3 7 3^7 37直接拿出去,然后剩下的这个对于 3 2 3^2 32取模,很显然是以模数为循环节的一个积的幂次,再乘上若干项多出来的
那这个就解决了
于是三个依次求解
求两个逆元
就结束了

long long exlucas(long long a,long long b,long long p)
{
	int cnt2=0;
	long long pp,tmp=p;
	for(int i=2;i*i<=p;i++)
	{
		if(p%i==0)
		{
			cnt++;
			cnt2=0;
			pp=1;
			while(p%i==0) cnt2++,p/=i,pp*=i;
			q[0]=1;
			for(int j=1;j<=pp;j++)
			{
				q[j]=q[j-1]*((j%i)?j:1)%pp;
			}
			r[cnt]=mul(b,i,pp)*inv(mul(a,i,pp),pp)%pp*inv(mul(b-a,i,pp),pp)%pp*kasumi(i,get(b,i)-get(a,i)-get(b-a,i),pp)%pp;
			c[cnt]=pp;
		}
	}
	if(p!=1) 
	{
		q[0]=1;
		for(int j=1;j<=p;j++)
		{
			q[j]=q[j-1]*((j%p)?j:1)%p;
		}
		r[++cnt]=mul(b,p,p)*inv(mul(a,p,p),p)%p*inv(mul(b-a,p,p),p)%p*kasumi(p,get(b,p)-get(a,p)-get(b-a,p),p)%p,c[cnt]=p;
	}
	long long ans=0;
	for(int i=1;i<=cnt;i++)
	{
		long long aa=tmp/c[i];
		long long bb=inv(aa,c[i]);
		ans=(ans+r[i]*aa%tmp*bb%tmp)%tmp;
	}
	return ans;
}

特殊的技巧:分段打表

c n m ( n < 1 e 10 , m < 1 e 10 , p < 1 e 10 ) c_n^m(n<1e10,m<1e10,p<1e10) cnm(n<1e10,m<1e10,p<1e10)
已知p

lucas的复杂度很显然由模数p决定,遇到这种虚假的情况自然会gg
我们可以考虑先打出1000000!%p,2000000!%p,3000000!%p…之类的
大概1000个数,然后就能做到1e7的单次处理
思路与分块略像

六、BSGS、EXBSGS

求x使 a x ≡ b ( m o d   p ) a^x\equiv b(mod \ p) axb(mod p) a,b,p<1e10

BSGS

本质是一个折半枚举
首先我们知道a的幂次模p的值是以 ϕ ( p ) \phi(p) ϕ(p)为循环节的

对p开根,记 m = p m=\sqrt{p} m=p
显然任何一个小于p的n
我们都可以用 n = x ∗ m − y n=x*m-y n=xmy表示
那么就相当于求 a x ∗ m − y ≡ b a^{x*m-y}\equiv b axmyb
也就是 a x ∗ m ≡ b ∗ a y a^{x*m} \equiv b*a^y axmbay
显然先把 b ∗ a y b*a^y bay扔到一个map里
然后枚举x匹配就行了,复杂度降到了 p \sqrt{p} p

	long long k=sqrt(p);
	long long up=p/k+2;
	long long now=b*a%p; 
	for(int i=1;i<=k;i++)
	{
		mm[now]=i;
		now=now*a%p;
	}
	now=kasumi(a,k);
	long long nnow=now;
	for(int i=1;i<=up;i++)
	{
		if(mm.find(nnow)!=mm.end())
		{
			printf("%lld\n",1ll*i*k-mm[nnow]);
			break;
		}
		nnow=nnow*now%p;
	}

七、原根

八、二次剩余

九、Miller-Rabin、Pollard_rho

Miller-Rabin

这玩意应该也算是生日悖论的一个应用,我们知道一些概率正确的判断条件,然后我们将这些条件组合,使得正确的概率接近|等于100%

首先两条定理
费马小定理
若p为质数则 a p − 1 ≡ 1 ( m o d   p ) a^{p-1}\equiv 1(mod \ p) ap11(mod p)
这玩意有个虚假的逆定理
a p − 1 ≡ 1 ( m o d   p ) a^{p-1}\equiv 1(mod \ p) ap11(mod p)则p为质数
这个定理是错的,但众所周知,逆否命题与原命题正确性相同
所以 a p − 1 ! ≡ 1 ( m o d   p ) a^{p-1}!\equiv 1(mod \ p) ap1!1(mod p)则p是合数肯定成立
二次探测
若p为质数则 x 2 ≡ 1 ( m o d   p ) x^2\equiv1(mod\ p) x21(mod p)的解只有 x = 1 或 x = p − 1 x=1或x=p-1 x=1x=p1
这个可以简短证明一下
x 2 − 1 ≡ 0 ( m o d   p ) x^2-1\equiv0(mod\ p) x210(mod p)
( x − 1 ) ( x + 1 ) (x-1)(x+1) (x1)(x+1)中有一个是p的倍数且p是质数,则x只能为1或者p-1
ok,逆否命题再次搬上来,反正不从1或者p-1平方得到1的p都是合数

有了这两个定理,我们把他们缝合起来,就是miller-rabin了
找到一组st使 2 s ∗ t = x − 1 2^s*t=x-1 2st=x1
然后从 a t a^t at开始不停地平方,二次探测,最后费马小定理
其实就是不停地筛去合数的可能性
多取几个a就基本上能判断了,复杂度是log乘个常数

	int p[]={2,3,5,7,11,13,17,19,23,29};
	long long s=x-1,t=0;
		if(x==0||x==1)
		{
			puts("N");
			continue;
		}
		else
		{
			if(x==2)
			{
				puts("Y");
				continue;
			}
		}
		while(!(s&1)&&s>0)
		{
			s>>=1;
			t++;
		}
		int flag=1;
		for(int i=0;i<10;i++)
		{
			long long tmp=kasumi(p[i],s);
			long long nxt;
			for(int j=1;j<=t;j++)
			{
				nxt=mul(tmp,tmp,x);
				if(nxt==1&&(tmp!=1&&tmp!=x-1)) 
				{
					flag=0;
				}
				tmp=nxt;
			}
			if(tmp!=1&&p[i]!=x) flag=0;
		}
		if(flag) puts("Y");
		else puts("N");

pollrad_rho

我认为大部分论文和博客将这两个算法一起提出的原因并不只有pollard_rho需要miller_rabin做前置,还因为两者有着一定的相同之处,比如说那个利用生日悖论原理的思想

多说无益,也就用了这么几条性质
假设分解n的质因数
第一步,使用miller_rabin判断素性
第二步,设计一个虚假的随机函数 f ( x ) = x 2 + c f(x)=x^2+c f(x)=x2+c如果gcd(f(x),n)>1,则gcd为n的一个因子
第三步,通过floyd判圈法找到循环节,重置c


十、莫比乌斯反演|欧拉反演

整除分块|数论分块

∑ i = 1 n [ n i ] \sum_{i=1}^n[\frac{n}{i}] i=1n[in]
可以注意到之前是商跳的快,i跳的慢
之后是商要跳好几个i才变一次
于是前半部分枚举i,后半部分枚举商
复杂度是 n \sqrt{n} n

	int up=sqrt(n);
	for(int i=1;i<=up;i++)
	{
		ans+=n/i;
	}
	up=n/(up+1);
	r=n;
	for(int i=1;i<=up;i++)
	{
		l=n/(i+1);
		ans+=i*(r-l);
		r=l;
	}

数论卷积|狄利克雷卷积

定义两个积性函数 f , g f,g f,g
狄利克雷卷积为 f ∗ g ( n ) = ∑ d ∣ n f ( d ) ∗ g ( n d ) f*g(n)=\sum_{d|n}f(d)*g(\frac{n}{d}) fg(n)=dnf(d)g(dn)

公式一

公式二

常见技巧

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = 1 ] \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==1] i=1nj=1m[gcd(i,j)==1]

注意到了等于一这个特性,又已知
∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
于是变成了
∑ d ∣ g c d ( i , j ) μ ( d ) = [ g c d ( i , j ) = = 1 ] \sum_{d|gcd(i,j)}\mu(d)=[gcd(i,j)==1] dgcd(i,j)μ(d)=[gcd(i,j)==1]
接着可以把枚举ij变成枚举d,显然i,都要是d的倍数
∑ d = 1 n μ ( d ) [ n d ] [ m d ] \sum_{d=1}^n\mu(d)[\frac{n}{d}][\frac{m}{d}] d=1nμ(d)[dn][dm]

∑ d = 1 n f ( d ) ∑ k = 1 [ n d ] μ ( k ) ∑ i = 1 [ n k d ] i \sum_{d=1}^nf(d)\sum_{k=1}^{[\frac{n}{d}]}\mu(k)\sum_{i=1}^{[\frac{n}{kd}]}i d=1nf(d)k=1[dn]μ(k)i=1[kdn]i

上式对任意一个n感觉需要nlogn求解
但可以简化
T = d k T=dk T=dk枚举 T T T
∑ T = 1 n ∑ i = 1 [ n T ] i ∣ ∑ d ∣ T f ( d ) μ ( T d ) ∣ \sum_{T=1}^n\sum_{i=1}^{[\frac{n}{T}]}i|\sum_{d|T}f(d)\mu(\frac{T}{d})| T=1ni=1[Tn]idTf(d)μ(dT)
显然打出||中的内容可以nlogn预处理
所以整个算式的复杂度就集中在了第二个求和上,可以感受到这是一个经典的数论分块
于是对于任意一个n,复杂度变成了 n \sqrt{n} n

十一、各种前缀和筛法

州阁筛

杜教筛

Min_25筛

∑ i = 1 n f ( i ) \sum_{i=1}^nf(i) i=1nf(i) n<1e9

min_25筛叫这个的原因是因为发明者叫min_25,别像笔者一样把自己绕进坑里
min_25筛的实质是一个高级dp,请换好dp思维

介于min_25筛求合数部分实属玄学
我们先单讲质数函数值的求解

质数函数值之和

假设 G ( m ) = ∑ i = 1 m [ i ∈ p r i m e ] F ( i ) G(m)=\sum_{i=1}^m[i\in prime]F(i) G(m)=i=1m[iprime]F(i)
我们设计一个 f ( i ) f(i) f(i)使 f ( i ) f(i) f(i)满足

f ( i ) f(i) f(i)是一个完全积性函数
f ( i ) f(i) f(i)在质数上的值与 F ( i ) F(i) F(i)相等
f ( i ) f(i) f(i)的前缀和可以迅速计算

考虑DP
g ( j , m ) g(j,m) g(j,m)为小于m的质数或者最小质因子大于p[j]的数的贡献
那么有两种情况
1. p [ j ] 2 > m p[j]^2>m p[j]2>m
显然不可能有一个合数的最小质因子是p[j],于是全是质数
g ( j , m ) = g ( j − 1 , m ) g(j,m)=g(j-1,m) g(j,m)=g(j1,m)
2. p [ j ] 2 < = m p[j]^2<=m p[j]2<=m
那么就是要额外减去p[j]是最小质因子的贡献
这一部分是什么呢
显然是 f ( p [ j ] ) ∗ ( g ( j , n / p [ j ] ) − ∑ k = 1 j − 1 f ( p [ k ] ) ) f(p[j])*(g(j,n/p[j])-\sum_{k=1}^{j-1} f(p[k])) f(p[j])(g(j,n/p[j])k=1j1f(p[k]))
于是答案是 g ( j − 1 , m ) − f ( p [ j ] ) ∗ ( g ( j , n / p [ j ] ) − ∑ k = 1 j − 1 f ( p [ k ] ) ) g(j-1,m)-f(p[j])*(g(j,n/p[j])-\sum_{k=1}^{j-1} f(p[k])) g(j1,m)f(p[j])(g(j,n/p[j])k=1j1f(p[k]))
这个开个滚动数组
然后一维维的dp下去就行了
可以发现的是

那么 G ( m ) = g ( k , m ) G(m)=g(k,m) G(m)=g(k,m)其中k为第一个满足p[k]^2大于m

几个应用
求区间质数数量
void min_25_count1()
{
    for(long long i=1,j;i<=n;i=j+1)
    {
        j=n/(n/i);
        st[++tot]=n/i;
        if(st[tot]<=m)
        {
            st1[st[tot]]=tot;
        }
        else
        {
            st2[n/st[tot]]=tot;
        }
        g[tot]=(st[tot]-1+p)%p;
    }
    for(int j=1;j<=cnt;j++)
    {
        for (int i=1; i<=tot && 1ll*pri[j]*pri[j]<=st[i]; i++)
        {
            long long tmp=st[i]/pri[j];
            tmp=(tmp<=m)?st1[tmp]:st2[n/tmp];
            g[i]=(g[i]-1ll*(g[tmp]-j+1)%p+p)%p;
        }
    }
}

求区间质数和
void min_25_count2()
{
    for(long long i=1,j;i<=n;i=j+1)
    {
        j=n/(n/i);
        st[++tot]=n/i;
        if(st[tot]<=m)
        {
            st1[st[tot]]=tot;
        }
        else
        {
            st2[n/st[tot]]=tot;
        }
        g[tot]=(st[tot]%p+2)*(st[tot]%p-1)%p*(p+1)/2%p;
    }
    for(int j=1;j<=cnt;j++)
    {
        for (int i=1; i<=tot && 1ll*pri[j]*pri[j]<=st[i]; i++)
        {
            long long tmp=st[i]/pri[j];
            tmp=(tmp<=m)?st1[tmp]:st2[n/tmp];
            g[i]=(g[i]-1ll*pri[j]*(g[tmp]-sum[j-1])%p+p)%p;
        }
    }
}

十二、快速数论变换 任意模数NTT

快速数论变换|NTT

NTT的所有知识还是与FFT相同
只不过把单位复数根换成了原根
可以感受到原根和单位复数根具有完全相同的性质
符合消去引理、折半引理和求和引理

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值