约数与同余

约数与同余

质数

1.质数分布定理:在1N中大约有xlnx个,N越大越精准

2.判断n为质数的方法:试除法:(可以顺带求出约数集合)

int t=sqrt(n);
if(n<2)return false;
for(int i=2;i<=t;i++){
    if(n%i==0)return false;
}
return true;

3.欧拉筛(线性筛质数):

int v[MAX_N],prime[MAX_N];
void primes(int n){
    memset(v,0,sizeof v); 
    int cnt=0;//质数数量 
    for(int i=2;i<=n;i++){
        if(!v[i]){
            v[i]=i,prime[++cnt]=i;
        } 
        for(int j=1;j<=cnt;j++){
            if(prime[j]>v[i]||prime[j]*i>n)
                break;
            v[i*prime[j]]=prime[j];

        }
    }
    for(int i=1;i<=cnt;i++)printf("%d ",prime[i]);
}

4.N!中质数p的个数为:

pkNNpk

约数

1.设N被算术基本定理分解为N=p1c1p2c2p3c3pmcm

N的正约数个数为:

(c1+1)×(c2+1)×(cm+1)=i=1m(ci+1)

和为:

i=1m(k=0ci(pi)k)=i=1m(pici+11pi1)

2.求1N中每个数的正约数集合:倍数法

vector<int>factor[MAX_N];
for(int i=1;i<=n;i++){
	for(int j=1;j<=n/i;j++){
		factor[i*j].push_back(i);
	}
}

时间复杂度为Θ(NlogN)

推论:1N中每个数的约数个数约为 NlogN

gcd

定理

a,bN    ,  lcm(x,y)=x×ygcd(x,y)

a,bN   , gcd(a,b)=gcd(b,amodb)

exgcd:

解决:求:ax+by=gcd(a,b)的一组解:

code:

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-(a/b)*y;
	return d;
}

欧拉函数 φ

定义:1N中与N互质的数的个数是关于N的欧拉函数,记作φ(N)

求法:

φ(N)=N×质数p|N(p1p)

性质:

1.1n中与n互质的数的和为:n×φ(n)2

2.若gcd(a,b)=1,则φ(ab)=φ(a)φ(b)

3.在算术基本定理中n=i=1m(pici),则φ(n)=k=1mφ(pici), 此性质所有积性函数都具备

4.若p为质数且p|n,p2|n,则φ(n)=φ(np)p

5.若p为质数且p|n,p2n,则φ(n)=φ(np)(p1)

6.d|nφ(d)=n

欧拉函数的递推求解:

由性质4,5,我们可以在求质数的时候顺带求出欧拉函数

int v[MAX_N],prime[MAX_N],phi[MAX_N];
void euler(int n){  
    memset(v,0,sizeof v);
    int cnt=0;
    for(int i=2;i<=n;i++){  
        if(!v[i]){  
            v[i]=i;
            prime[++cnt]=i;
            phi[i]=i-1;        
        }
        for(int j=1;j<=cnt;j++){  
            if(prime[j]>v[i]||i*prime[j]>n)break;
            v[i*prime[j]]=prime[j];
            phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
        }
    }
}

同余

1.欧拉定理

若正整数a,n互质,则aφ(n)1(modn)

推论:

若正整数a,n互质,则ababmodφ(n)(modn)

当a,n不一定互质的时候有(扩展欧拉定理):

ababmodφ(n)+φ(n)(modn)

2.裴蜀定理: 对于任意整数a,b,存在一对整数x,y满足:ax+by=gcd(a,b)

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-(a/b)*y;
	return d;
}

对于更加一般的一次不定方程ax+by=c,设d=gcd(a,b),它有解当且仅当d|c
我们可以先用exgcd求出方程ax+by=d的一组解x0,y0,这样方程ax+by=c的解x=x0×cd,y同理
由此我们可以设方程ax+by=c的一组解为x0,y0,则它的所有整数解为:

x=x0+kbd,y=y0kad,kZ

乘法逆元

定义,设bd1(modn),则b为d关于模n的乘法逆元,d为b关于模n的乘法逆元

求法:1.若n是质数,则d=bn2

2.若仅仅是保证b,n互质,则d可通过求解线性同余方程求得

解线性同余方程

对于线性同余方程axb(modm),可以将其看作ax+ym=b的不定方程来解,这里的x就是线性同余方程的解

中国剩余定理

m1,m2,m3mn是两两互质的整数,设M=i=1nmi,Mi=Mmi,设ti是线性同余方程Miti1(modmi)的一个解
则对于同余方程组:

{xa1(modm1)xa2(modm2)xa3(modm3)xan(modmn)

有解,解为:

x=i=1naiMiti+kM,kZ

 
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
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),z=x;
	x=y,y=z-(a/b)*y;
	return d;
}
int n,M[100005],m[100005],t[100005],a[100005],M1=1;
int CRT(){
	for(int i=1;i<=n;i++)M1*=m[i];
	for(int i=1;i<=n;i++)M[i]=M1/m[i];
	for(int i=1;i<=n;i++){
		int x,y;
		exgcd(M[i],m[i],x,y);
		t[i]=x;
		t[i]=(x%m[i]+m[i])%m[i];
	}
	int ans=0;
	for(int i=1;i<=n;i++)ans+=M[i]*t[i]*a[i],ans%=M1; 
	return ans;
} 
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&m[i],&a[i]);
	printf("%lld\n",CRT()%M1);
}

扩展中国剩余定理(excrt)

特别的,当m1mn并不两两互质的时候
假设我们已经求出了前k1个方程的通解,记m=lcm(m1mk1),则x+im,iZ是方程的通解,此时我们需要求出一个t使得x+tmak(modmk),此时x+tm就是前k个方程的解,所以其实我们可以将excrt理解为n次exgcd

高次同余方程:axb(modp)

拔山盖世(大步小步:Baby Step,Giant Step)算法

前提:a,p互质

因为a,p互质,所以可以在模p的意义下进行关于a的乘除运算
x=i×tj,t=p,i[0,t],j[0,t1]
则方程式子可变为:(at)iaj×b(modp),此时枚举j的取值,将aj×b插入一个Hash表中,然后再枚举i在表中查找即可

int BSGS(int a,int b,int p){
	int t=sqrt(p)+1;b%=p,a%=p;
	map<int,int>hash;hash.clear();
	for(int i=0;i<t;i++){
		hash[power(a,i,p)*b%p]=i;
	}
	a=power(a,t,p);
	if(!a)return b==0?1:-1; 
	for(int i=0;i<=t;i++){
		int m=power(a,i,p);
		int j=hash.find(m)==hash.end()?-1:hash[m];
		if(j>=0&&i*t-j>=0){
			return i*t-j;
		}
	}
	return -1;
} 

exBSGS

Code
:

int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int exgcd(int a,int b,int &x,int &y)
{
	if(!b)
	{
		x=1,y=0;
		return a;
	}
	int gcd=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return gcd;
}
inline int exBSGS(int a,int b,int mod)
{
	b%=mod;
	if(b==1||mod==1)	return 0;
	int g=0,val=1;
	while(true)
	{
		int d=gcd(a,mod);
		if(d==1)	break;
		if(b%d)	return -1;
		b/=d,mod/=d,val=val*a/d%mod;
		g++;
		if(b==val)	return g;
	}
	int x,y;
	exgcd(val,mod,x,y);
	x=(x%mod+mod)%mod;
	b=b*x%mod,a%=mod;
	map<int,int>mp;
	int t=ceil(sqrt(mod));
	int z=1;
	for(int i=0;i<t;i++)
		mp[b*z%mod]=i,z=z*a%mod;
	int p=1;
	for(int i=1;i<=t;i++)
	{
		p=p*z%mod;
		if(mp.count(p))	return i*t-mp[p]+g;
	}
	return -1;
}
signed main()
{
	a=read(),p=read(),b=read();
	while(a||b||p)
	{
		int ans=exBSGS(a,b,p);
		if(ans==-1)	puts("No Solution");
		else	printf("%lld\n",ans);
		a=read(),p=read(),b=read();
	}
	return 0;
}

posted @   spdarkle  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示