2025.3.1数学听课笔记

质数

一到 n 以内约有 nlogn 个质数
可以在 n 的时间下判断一个数是否为质数
i=1n1ilnn
质数筛法:埃氏筛和欧拉筛
埃氏筛:从小到大枚举,将他所有的非平凡倍数标记为“非质数”。
欧拉筛:考虑让每个数x的最小质因子 p ,让 x(xp) 通过 ×p 筛掉。
具体做法:记录每个数 x 的最小质因子 p ,枚举每个每一个比 p 小的最小质因子ei,筛去 ei×x
代码:

e[i] //下标为最小质因子 e[i]为i是第几个质数 
prime[i] //质数 
for(int i=2;i<=n;i++){
	if(!e[i]) prime[e[i]=++tot]=i;
	for(int j=1;j<=e[i] && prime[j]*i<=n;j++){
		e[prime[j]*i]=j;
	}
} 

质因数分解

n 暴力分解

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e5+50;
int ans[N],cnt[N],tot=1;
bool f=false;
int main(){
	int n,x;
	cin>>n;
	x=n;
	for(int i=2;x!=1;i++){
		f=false;
		while(x%i==0){
			x/=i;
			cnt[tot]++;
			ans[tot]=i;
			f=true;
		}
		if(x==1) break;
		if(f) tot++;
	}
	for(int i=1;i<=tot;i++){
		cout<<ans[i]<<' '<<cnt[i]<<'\n';
	}
	return 0;
}

O(V) 预处理,O(logn)分解

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e5+50;
const int V=1e7;
int e[V+50],prime[N],cnt[N],idx,idx2=1;
int ans[N];
int n,tot;
bool f=false;
int main(){
	cin>>n;
	for(int i=2;i<=n;i++){
		if(!e[i]) prime[e[i]=++tot]=i;
		for(int j=1;j<=e[i] && prime[j]*i<=n;j++){
			e[prime[j]*i]=j;
		}
	}
	int nn=n;
	while(nn!=1){
		idx++;
		f=false;
		while(nn%prime[idx]==0){
			nn/=prime[idx];
			cnt[idx2]++;
			ans[idx2]=prime[idx];
			f=true;
		}
		if(nn==1) break;
		if(f) idx2++;
	}
	for(int i=1;i<=idx2;i++){
		cout<<ans[i]<<' '<<cnt[i]<<'\n';
	}
	return 0;
}

约数(因数)

约数个数:质因子分解后,(指数+1)的乘积。
i=1s(αi+1)
约数和:质因子分解后,1+p+p2++pai 的乘积
i=1s(j=0αipij)
通过质因数分解求因数:写一个 dfs ,枚举每一个质因子的指数。即可求解出该数的所有因子。

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e5+50;
const int V=1e7;
int e[V+50],prime[N],cnt[N],idx,idx2=1;
int ans[N];
bool fac[V];
int n,tot;
bool f=false;
void dfs(int x,int d){
	if(d>idx2+1) return ;
	fac[x]=1;
	for(int i=1;i<=cnt[d];i++){
		dfs(x*pow(ans[d],i),d+1);
	}
	return ;
}
int main(){
	cin>>n;
	for(int i=2;i<=n;i++){
		if(!e[i]) prime[e[i]=++tot]=i;
		for(int j=1;j<=e[i] && prime[j]*i<=n;j++){
			e[prime[j]*i]=j;
		}
	}
	int nn=n;
	while(nn!=1){
		idx++;
		f=false;
		while(nn%prime[idx]==0){
			nn/=prime[idx];
			cnt[idx2]++;
			ans[idx2]=prime[idx];
			f=true;
		}
		if(nn==1) break;
		if(f) idx2++;
	}
	for(int i=1;i<=idx2;i++){
		cout<<ans[i]<<' '<<cnt[i]<<'\n';
	}cout<<'\n';
	for(int i=1;i<=idx2;i++){
		dfs(1,i);
	}
	for(int i=1;i<=n;i++){
		if(fac[i]){
			cout<<i<<' ';
		}
	}
	return 0;
}

gcd & lcm

gcd:最大公因子
lcm:最小公倍数
lcm(x,y)=x×ygcd(x,y)
x=ga,y=gb,gcd(x,y)=g,lcm(x,y)=g×a×b。条件gcd(a,b)=1
在质因子分解下为指数上的取 minmax
gcd 求法:辗转相除
gcd(x,y)=gcd(xy,y)>gcd(x,y)=gcd(x%y,y)

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int Gcd(int x,int y){
	while(y!=0){
		int tmp=x%y;
		x=y;
		y=tmp;
	}
	return x;
}
int main(){
	int x,y;
	cin>>x>>y;
	cout<<Gcd(x,y);
	
	return 0;
}

欧拉函数

互质:两个数的最大公因数为 1
欧拉函数:φ(n)=1n 中与 n 互质的个数
φ(n)=(p11)p1a11×(p21)p2a21××(pk1)pkak1
φ(n)=n×i=1spi1pi
n 分解为 a , bab 互质时,f(n)=f(a)×f(b) 此时称 f(x) 为积性函数
φ(n) 为积性函数
下面是 φ(n) 的求解代码,用于求单个值的欧拉函数,时间复杂度应为 O(n):

//求单个值的欧拉函数
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int phi(int n){
	int ans=n;
	for(int i=2;i*i<=n;i++){
		if(n%i==0){
			ans=ans/i*(i-1);
			while(n%i==0) n/=i;
		}
	}
	if(n>1) ans=ans/n*(n-1);
	return ans;
}
int main(){
	int n;
	cin>>n;
	cout<<phi(n);
	return 0;
}

这个是线性求 n 个值的欧拉函数,时间复杂度是 O(n)

int phi[N];
bool prime[N];
vector<int> pri;
void varphi(){
	phi[1]=1;
	for(int i=2;i<=n;i++){
		if(!prime[i]){
			pri.push_back(i);
			phi[i]=i-1;
		}
		for(int k=0;k<(int)pri.size();k++){
			int j=pri[k];
			if(i*j>n) break;
			prime[i*j]=1;
			if(i%j==0){
				phi[i*j]=phi[i]*j;
				break;
			}
			phi[i*j]=phi[i]*phi[j];
		}
	}
}

同余

-在模 p 的意义下,a 模等于 b 当且仅当 p 整除(ab)>(ab)%p=0
同余的性质
对于整数 abc 和自然数mn ,对模m同余满足:
1.自反性:a%m=a%m
2.对称性:若a%m=b%m,则b%m=a%m
3.传递性:若a%m=c%mb%m=c%m,则a%m=b%m
4.同加性:若a%m=b%m(a+c)%m=(b+c)%m
5.同乘性:若a%m=b%m(a×c)%m=(b×c)%m
6.同幂性:若a%m=b%mac=bc(modm)
!同余没有可除性!

欧拉定理

若正整数 an 互质,则 aφ(n)=1(modn) ,模数 n 可以不为质数,但需要与 a 互质。

广义欧拉定理

对于所有 a , m ,若 gcd(a,m) 不等于 1 ,则有

anmodm={anmodmif(n<φ(m))anmodφ(m)+φ(m)modmif(nφ(m))

【模板】扩展欧拉定理

模板题,套上述公式即可。什么你想知道超级大数 n 怎么取模 φ(m) ?我也不会啊。
介绍一下现学的大整数取模:
对于一个数 n 我们令 n=n1×10len1+n2×10len2nlen1×101+nlen×1 。说人话就是把它拆成每一位数,对于同余,我们有同乘性和同加性,所以我们只需要对每一位数取模,再对他们的和取模就可以啦!

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
#define int long long
using namespace std;
int a,mo;
int phi(int n){
	int ans=n;
	for(int i=2;i*i<=n;i++){
		if(n%i==0){
			ans=ans/i*(i-1);
			while(n%i==0) n/=i;
		}
	}
	if(n>1) ans=ans/n*(n-1);
	return ans;
}
int qpow(int a,int b){
	int ans=1,k=a;
	while(b){
		if(b&1) ans=ans*k%mo;
		b=b>>1;
		k=k*k%mo;
	}
	return ans%mo;
}
int qread(int mo){
	char c=getchar();
	int x=0;
	bool f=false;
	while(c<'0' || c>'9') c=getchar();
	while(c>='0' && c<='9'){
//              高级快读,(x<<3)+(x<<1) 相当于 x*=10,(c^'0') 相当于 c-='0'
		x=(x<<3)+(x<<1)+(c^'0');
		if(x>=mo){
			x%=mo,f=true;
		}
		c=getchar();
	}
	if(f) return x+mo;
	else return x;
}
signed main(){
	cin>>a>>mo;
	int b=phi(mo);
	int c=qread(b);
	cout<<qpow(a,c);
	return 0;
}

裴蜀定理

形如 a×x+b×y=c(xN,yN) 有解的充要条件是 gcd(a,b)|c 也就是 c%gcd(a,b)=0

【模板】裴蜀定理

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int gcd(int x,int y){
	return y ? gcd(y,x%y) : x;
//  题解区最最最优美的 gcd 求法
}
int n,ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int a;cin>>a;
		a= a>=0 ? a : -a;
		ans=gcd(ans,a);
	}
	cout<<ans;
	return 0;
}

逆元

gcd(A,p)=1,A×x=1(modp), x 则为逆元,逆元是唯一的。
费马小定理,扩展欧几里得(EXgcd)。
下面是扩展欧几里得(exgcd)

a×x+b×y=1
b×x+(a%b)×y=1
b×x+(a(a/b)×b)×y=1
a×y+b×(x(a/b)×y)=1
x=y,y=x(a/b)×y

发现上式在 y=0 时必然有一组解为 x=1 。递归去做这件事。
解出来的 x 即为逆元。

同余方程

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
long long x,y;
void exgcd(long long a,long long b){
	if(b==0){
		x=1;
		y=0;
		return ;
	}
	exgcd(b,a%b);
	long long t=x;
	x=y;
	y=t-a/b*y;
	return ;
}
int main(){
	long long a,b;
	cin>>a>>b;
	exgcd(a,b);
	x=(x%b+b)%b;
	cout<<x;
	return 0;
}

中国剩余定理(EXCRT)

给定 n 组非负整数 ai , bi ,求解关于 x 的方程组的最小非负整数解

x=b1(moda1)
x=b2(moda2)

x=bn(modan)

【模板】中国剩余定理(CRT)/ 曹冲养猪

如果所有的 ai 两两互质,那么可以使用正常的 CRT 求解。
具体详见第一篇题解

#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N=15;
int read(){
	bool f=false;
	int x=0;
	char c=getchar();
	if(c<'0' || c>'9'){if(c=='-') f=true;c=getchar();}
	while(c>='0' && c<='9'){
		x=(x<<3)+(x<<1)+(c-'0');
		c=getchar();
	}
	return f==0 ? x : -x;
}
void print(int ans){
	if(ans==0) cout<<0;
	else{
		string s;
		while(ans){
			s+=ans%10+'0';
			ans/=10;
		}
		for(int i=s.length()-1;i>=0;i--){
			cout<<s[i];
		}
	}
	return ;
}
int exgcd(int a,int b,int &x,int &y){
	if(!b){
		x=1;
		y=0;
		return a;
	}
	int res=exgcd(b,a%b,x,y);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return res;
}
int n;
int a[N],b[N];
int ans=1,res;
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		b[i]=read();
		ans*=a[i];
	}
	for(int i=1;i<=n;i++){
		int k=ans/a[i];
		int x,y;
		int gcd=exgcd(k,a[i],x,y);
		res=res+k*b[i]*x%ans;
	}
	print((res%ans+ans)%ans);
	return 0;
}

【模板】扩展中国剩余定理(EXCRT)

解法:
x=k1×a1+b1
x=k2×a2+b2
k1×a1k2×a2=b2b1
已知 a1,b1,a2,b2 求解 k1,k2
有想法了吗?是上述的 exgcd 对吧。对于原方程组做若干次 exgcd 就有解了。(证明请移步题解区第一篇,以及还有一些重要观察。)
本题还需要一个龟速承,原理是把一个十进制数转成二进制数去做,去凑出每一位的数,可以看看代码理解一下。

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
#define int long long
using namespace std;
const int N=1e5+10;
int aa[N],bb[N];
int n;
int read(){
	int x=0;bool f=false;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') f=1;c=getchar();}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^'0');c=getchar();}
	return f==0?x:-x;
}
int smul(int a,int b,int mo){
	int res=0;
	while(b>0){
		if(b&1) res=(res%mo+a%mo)%mo;
		a=(a%mo+a%mo)%mo;
		b>>=1;
	}
	return res;
}
int exgcd(int a,int b,int &x,int &y){
	if(b==0) {x=1;y=0;return a;}
	int gcd=exgcd(b,a%b,x,y);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return gcd;
}
int excrt(){
	int x,y;
	int lcm=bb[1],ans=aa[1];
	for(int i=2;i<=n;i++){
		int a=lcm;
		int b=bb[i];
		int c=(aa[i]-ans%b+b)%b;
		int gcd=exgcd(a,b,x,y);
		int bgcd=b/gcd;
		if(c%gcd!=0) return -1;
		x=smul(x,c/gcd,bgcd);
		ans+=x*lcm;
		lcm*=bgcd;
		ans=(ans%lcm+lcm)%lcm;
	}
	return (ans%lcm+lcm)%lcm;
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		bb[i]=read();
		aa[i]=read();
	}
	cout<<excrt();
	return 0;
}

BSGS 北上广深算法(确信)

给定一个质数 p ,以及一个整数 a ,一个整数 b ,现在要求你计算一个最小的非负整数 l ,满足al=b(modp)

[TJOI2007] 可爱的质数/【模板】BSGS

朴素做法枚举 l ,写出如下代码:

#include<iostream>
#define int long long
using namespace std;
int a,b,p;
int ans=1;
signed main(){
	cin>>p>>a>>b;
	for(int i=1;i<=(p<<1);i++){
		ans=(ans*a)%p;
		if(ans==b){
			cout<<i;
			return 0;
		}
	}
	
	cout<<"no solution";
	return 0;
}

收获如下好成绩
接下来就是 BSGS 算法了:分块(优雅的暴力)。
原式为:al=b(modp),化为 aAmn=b(modp),左右两侧同乘 an,式子变为:aAm=ban(modp)
我们可以预处理出所有的 banmodp 存入 hash 中,再去计算 aAmmodp 然后去查 hash 表中的数是否存在相同的数。 其他的详见代码。欸嘿我不会 hash ,我用 map。
用 hash 的时间复杂度是 O(p) 。用 map 是 O(p×logp)

#include<iostream>
#include<map>
#include<cmath>
#define int long long
using namespace std;
map<int,int> mp;
int qpow(int a,int b,int p){
	int ans=1;
	while(b>0){
		if(b&1) ans=(ans*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return ans;
}
int BSGS(int a,int b,int p){
	if(a%p==b%p) return 1;
	if(a%p==0 && b!=0) return -1;
	int len=(long long)sqrt(p)+1;
//  预先求出a^sqrt(p) 
	int tmp=qpow(a,len,p);
//  求解 b*a^(0->sqrt(p))
	for(int i=0;i<=len;i++){
		mp[b]=i;
		b=(b*a)%p;
	}
	b=1;
//  求解 b*a^{ (0->sqrt(p))*sqrt(p) }
	for(int i=1;i<=len;i++){
		b=(b*tmp)%p;
		if(mp[b]) return i*len-mp[b];
	}
	return -1;
}
int p,a,b;
signed main(){
	cin>>p>>a>>b;
	int ans=BSGS(a,b,p);
	if(ans==-1) cout<<"no solution";
	else cout<<ans;
	return 0;
}

例题

AT_ACL_Contest_1 B-sum is multiple

求最小的 k 满足 k×(k+1)%(2×n)=0
跟数竞生一顿商讨后,他们告诉我要构造CRT,那就试试吧。
a×b=2×n 此时可得出

x={k=0(moda)k+1=0(modb)

k=a×x,代入上式化简得到:

a×x=1(modb)

枚举 a,b ,直接上 exgcd 求解 x
注意开 __int128。

#include<iostream>
#define int long long
#define ll __int128
using namespace std;
int n;
int ans;
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	ll res=exgcd(b,a%b,x,y);
	ll tmp=x;
	x=y;
	y=tmp-a/b*y;
	return res;
}
ll ask(ll a,ll b){
	ll x=0,y=0;
	ll gcd=exgcd(a,b,x,y);
	if((b-1)%gcd!=0){
		return 1e18;
	}
	x=(x%b*(b-1)%b+b)%b;
	if(!x){
		x+=b;
	}
	if(a*x>ans){
		return 1e18;
	}
	return a*x;
}
signed main(){
	cin>>n;
	n*=2;
	ans=n-1;
	for(int i=1;1ll*i*i<=n;i++){
		if(n%i==0){
			ans=min((ll)ans,min(ask(i,n/i),ask(n/i,i)));
		}
	}
	cout<<ans;
	return 0;
}

沙拉公主的疑惑

给定mn,求 [1,n!] 中有多少数与 m! 互质,答案对质数 p 取模,n1e7,1e4组测试数据。

咕着

多少个1?

若干个 11111(N) 的形式,思考一下可以转化为 10n19 的形式。
那么原题就变为求:

10n19=k(modm)

略微化简,得到如下式子:

10n=9×k+1(modm)

求最小整数 n 满足上述式子,套BSGS板子。
中间需要开 __int128 ,或者使用快速乘。
原理是乘法分配律,可以试着推一把(反正我没推)

#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
unordered_map<int,int> mp;
int mul(int a,int b,int p){
	int l=a*(b>>25ll)%p*(1ll<<25)%p;
	int r=a*(b&((1ll<<25)-1))%p;
	return (l+r)%p;
}
int qpow(int a,int b,int p){
	int res=1;
	while(b){
		if(b&1) res=mul(res,a,p);
		a=mul(a,a,p);
		b>>=1;
	}
	return res;
}
int BSGS(int a,int b,int p){
	int len=ceil(sqrt(p));
	for(int i=0;i<len;i++) mp[mul(b,qpow(a,i,p),p)]=i;
	int tmp=qpow(a,len,p);
	for(int i=0;i<=len;i++){
		int x=qpow(tmp,i,p);
		int k=mp.count(x) ? mp[x] : -1;
		if(k>=0 && i*len-k>=0) return i*len-k;
	}
	return -1;
}
int n,m;
signed main(){
	cin>>n>>m;
	n=n*9+1;
	n%=m;
	cout<<BSGS(10,n,m);
	return 0;
}

仪仗队

可观测的坐标需要 (x,y) 互质,直接求小于 x 的质因子个数 φ(x) 最后答案:i=1n1φ(i)+1
需要线性求出 1n 的欧拉函数。

#include<iostream>
#include<vector>
using namespace std;
const int N=40050;
int n;
long long ans=0;
int phi[N];
bool prime[N];
vector<int> pri;
void varphi(){
	phi[1]=1;
	for(int i=2;i<=n;i++){
		if(!prime[i]){
			pri.push_back(i);
			phi[i]=i-1;
		}
		for(int k=0;k<(int)pri.size();k++){
			int j=pri[k];
			if(i*j>n) break;
			prime[i*j]=1;
			if(i%j==0){
				phi[i*j]=phi[i]*j;
				break;
			}
			phi[i*j]=phi[i]*phi[j];
		}
	}
}
int main(){
	cin>>n;
	varphi();
	for(int i=1;i<n;i++){
		ans+=phi[i];
	}
	if(n==1) cout<<0;
	else cout<<ans*2+1;
	return 0;
}

反质数

咕着

posted @   Tighnari  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
  1. 1 イエスタデイ(翻自 Official髭男dism) 茶泡饭,春茶,kobasolo
イエスタデイ(翻自 Official髭男dism) - 茶泡饭,春茶,kobasolo
00:00 / 00:00
An audio error has occurred.

作词 : 藤原聡

作曲 : 藤原聡

何度失ったって

取り返して見せるよ

雨上がり 虹がかかった空みたいな

君の笑みを

例えばその代償に

誰かの表情を

曇らせてしまったっていい

悪者は僕だけでいい

本当はいつでも

誰もと思いやりあっていたい

でもそんな悠長な理想論は

ここで捨てなくちゃな

遥か先で 君へ 狙いを定めた恐怖を

遥か先で 君へ 狙いを定めた恐怖を

どれだけ僕は

はらい切れるんだろう?

半信半疑で 世間体

半信半疑で 世間体

気にしてばっかのイエスタデイ

ポケットの中で怯えたこの手は

まだ忘れられないまま

「何度傷ついたって

「何度傷ついたって

仕方ないよ」と言って

うつむいて君が溢した

儚くなまぬるい涙

ただの一粒だって

僕を不甲斐なさで 溺れさせて

理性を奪うには十分過ぎた

街のクラクションもサイレンも

街のクラクションもサイレンも

届きやしないほど

遥か先へ進め 身勝手すぎる恋だと

遥か先へ進め 身勝手すぎる恋だと

世界が後ろから指差しても

振り向かず進め必死で

振り向かず進め必死で

君の元へ急ぐよ

道の途中で聞こえたSOS さえ

気づかないふりで

バイバイイエスタデイ ごめんね

バイバイイエスタデイ ごめんね

名残惜しいけど行くよ

いつかの憧れと違う僕でも

ただ1人だけ 君だけ

守るための強さを

何よりも望んでいた この手に今

遥か先へ進め

遥か先へ進め

幼すぎる恋だと

世界が後ろから指差しても

迷わずに進め 進め

2人だけの宇宙へと

ポケットの中で震えたこの手で今

君を連れ出して

未来の僕は知らない

だから視線は止まらない

謎めいた表現技法

意味深な君の気性

アイラブユーさえ

アイラブユーさえ

風に 飛ばされそうな時でも

不器用ながら繋いだ この手はもう

決して離さずに

虹の先へ

点击右上角即可分享
微信分享提示