数论浅略总结

数论总结

前记:

由于是数论总结 所以肯定有地方是不够严谨/根本没证
(靠 怎么没换行 不管了)

CRT与扩展CRT

CRT:

求解线性一元同余方程组: 满足\(m\)互质

\[x \equiv c1 \ (mod \ m1)\\ x \equiv c2 \ (mod \ m2)\\ ... \]

求解方法:

中国剩余定理(CRT)及其扩展(EXCRT)详解 - ailanxier - 博客园 (cnblogs.com)

(太懒了)

扩展CRT:

问题同上 但是\(m\)不互质

我们将取模去掉 得到:

\[x=c1+m1k1 \\ x=c2+m2k2 \]

联立得:\(m1k1=c2-c1+m2k2\)

当且仅当\((m1,m2)|(c2-c1)\)时有解

\(d=(m1,m2)\) 使得等式两边同时除以\(d\) 得:
\(\frac{m1}{d}k1=\frac{c2-c1}{d}+\frac{m2}{d}k2\)

同时取模\(\frac{m2}{d}\) 得:
\(\frac{m1}{d}k1 \equiv \frac{c2-c1}{d} \ mod(\ \frac{m2}{d})\)

\(\frac{m1}{d}\)移项至右边 得:
\(k1=inv(\frac{m1}{d},\frac{m2}{d}) * \ \frac{c2-c1}{d}+ y\frac{m2}{d}\)

\(k1\)带入原式(\(x=c1+m1k1\))中 得:

\(x \ \equiv \ inv(\frac{m1}{d},\frac{m2}{d})\ * \ \frac{c2-c1}{d}\ *\ m1+c1\ mod(\ \frac{m1m2}{d})\)

发现所有量都变成了已知量 直接求解即可 具体而言 有\(n\)条式子就需要进行\(n-1\)

int gcd(int x,int y){
	if(y==0)return x;
	return gcd(y,x%y);
}
int lcm(int x,int y){
	return x/gcd(x,y)*y;
}
int exgcd(int x,int y,int &s1,int &s2){
	if(y==0){
		s1=1,s2=0;
		return x;
	}
	int d=exgcd(y,x%y,s1,s2);
	int temp=s1;
	s1=s2,s2=temp-x/y*s2;
	return d;
}
int mul(int x,int p,int mod){
	if(p<0)x=-x,p=-p;
	int val=0;
	for(int i=p;i;i/=2){
		if(i&1)val=(val+x)%mod;
		x=(x+x)%mod;
	}
	return val;
}
int solve(){
	int R=r[1],M=a[1];
	for(int i=2;i<=k;i++){
		int lst_m=M,now_m=a[i],c=r[i]-R,x,y;
		int d=exgcd(lst_m,now_m,x,y);
		if(c%d)return -1;
		lst_m/=d,now_m/=d,c/=d;
		x=((mul(x,c,now_m)+now_m)%now_m)%now_m;
		R=R+x*M;M=M*a[i]/d;R=(R+M)%M;
	}
	R=(R+M-1)%M+1;
	if(R+k-1>m)return -1;
	return R;
}

(从这以后的'*'全都代表卷积 懒的改了)

欧拉函数:

定义 \(\varphi\) 为欧拉函数 其计算方式为:$\varphi(n)=\sum_{i=1}^{n}[gcd(n,i)==1] $ 即与\(n\)互质的个数 特别的 \(1\)算入在内

值得一提的是 欧拉函数是一个积性函数

有一数论函数$f(x) $ 满足\(f(ab)=f(a)f(b)\)\((a,b)=1\)时 我们称其为积性函数

数论函数:可以粗浅的理解为定义域在正整数上的函数

特别的 若数论函数满足对于任意\(a,b\)均满足\(f(ab)=f(a)f(b)\) 那么我们称其为完全积性函数

一般而言 欧拉函数的计算方式为:\(\varphi(n)=n\prod (1-\frac{1}{p_i})\) 其中\(p_i\)\(n\)的质因子

它是一个欧拉函数 也就意味着它可以在线性时间被求解 具体而言:(\(p\)为质数)
\(p|n\)\(p^2|n\) 此时 \(\varphi(n \times p)=\varphi(n)\times p\) 用上面那个式子拆开即可得到

\(p|n\)\(p^2\nmid n\) 那么\(\varphi(n \times p)=\varphi(n)\times \varphi(p)\) 由积性函数性质可知

当然 它还有一个很重要的性质:

\(\varphi(n)*1=Id(n)\)

定义:\(*\)为卷积符号(此处专值狄利克雷卷积)

卷积定义:
设有两个数论函数\(f(a),g(a)\) 那么他们的卷积就是:\(\sum_{d|n} f(d)g(\frac{n}{d})\)

\(Id(i)\)为基本函数 其取值为\(Id(i)=i\) (好SB)

这个性质会在杜教筛里面用到

莫比乌斯函数:

$\mu $为莫比乌斯函数

对于一个数\(d\),有\(d=p_1^{c_1}p_2^{c_2}..\)

其取值为:

\[\mu = 1\ (d=1)\\ (-1)^k \ (\prod c_i=1)\\ 0\ (other) \]

换句话说就是:为1则函数值为1,,每个质因子只出现一次且质因子个数为奇数则为-1,为偶数则为 1;出现了不止一次则为0

当然 这玩意也是积性函数 同样可以线性求

其有一个极其重要的性质:

\(\sum_{d|n}\mu (d)=[n=1]\)

考虑证明:
\(\sum_{d|n}\mu (d)=\sum_{i=0}^{k}(-1)^iC_{k}^{i}\) 直接二项式定理解决

莫比乌斯反演

其有一基本形式:

\[F(n)=\sum_{d|n}f(d)\\ f(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) \]

\(F\)回套入\(f\)中即可

有一拓展形式:
\(f(i)=\sum_{d=1}^{\lfloor \frac{n}{i} \rfloor }g(d\times i)\)

那么\(g(i)=\sum_{d=1}^{\lfloor \frac{n}{i} \rfloor }f(d\times i)\mu(d)\)

考虑证明:

\[原式子=\sum_{d=1}^{\lfloor \frac{n}{i} \rfloor }(\sum_{j=1}^{\lfloor \frac{n}{di} \rfloor }g(i\times j\times d)\mu(d)) \\ =\sum_{k=1}^{\lfloor \frac{n}{i} \rfloor }g(k\times i)\sum_{p|k}\mu(p)\\ 当且仅当k=1时 \sum\mu为1 即: \\=g(i) \]

定义:卷积单位元为:\(\varepsilon(i)=[i==1]\)

有:$\mu*1= \varepsilon $ 证明显然

莫比乌斯函数还有一常见转换:
\([gcd(i,j)==1]->\)\(\varepsilon(gcd(i,j))=\sum_{d|gcd(i,j)}\mu(d)\)

剩下的要做题 还没做呢 做到了再说

杜教筛:

可以在\(O(n^{\frac{2}{3}})\)时间内求解某积性函数的前缀和

条件较为苛刻:

假如\(f(n)\)为积性函数 若能找到另一个积性函数\(g(n)\) 使得\(g(n)\)\((f*g)(n)\)都可以快速求出 那么就可以求出\(f(n )\)

具体求解:
定义\(S(n)=\sum_{i=1}^{n}f(i)\) 有基本事实:\((f*g)(i)=\sum{d|i}f(d)g(\frac{n}{d})=\sum_{x=1}\sum_{xy=i}f(x)g(y)\)

发现可以提项数 那么改写为:

\[\sum_{i=1}^{n}(f*g)(i)=\\ \sum_{x=1}g(x)\sum_{xy=1} f(y)\\ = \sum_{x=1}g(x)S(\lfloor \frac{n}{x}\rfloor)\\ =g(1)S(n)+\sum_{x=2}^{n}g(x)S(\lfloor \frac{n}{x}\rfloor) \]

移项即得:
\(g(1)S(n)=\sum_{i=1}^{n}(f*g)(i)-\sum_{x=2}^{n}g(x)S(\lfloor \frac{n}{x}\rfloor)\)

后半部分整除分块+递归即可 前半部分直接求解 因为杜教筛的前提就是\((f*g)(n)\)可快速求出

例如欧拉函数 由于\(\varphi*1=Id\) 所以直接定义\(g=1\) 公式直接减缩为:
\(S(n)=\frac{n(n+1)}{2}-\sum_{y=2}^{n}S(\lfloor \frac{n}{y} \rfloor )\)

莫比乌斯函数类似 由于$\mu *1=\varepsilon $ 仍定义\(g=1\) 化简得:
\(S(n)=1-\sum_{y=2}^{n}S(\lfloor \frac{n}{y} \rfloor )\)

附上模板:(当\(O(n^{\frac{2}{3}})\)时 欧拉筛所刷长度也需要为\(O(n^{\frac{2}{3}})\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=5e6+10;
const int INF=0x3f3f3f3f;
int t,n,mu[maxn],vis[maxn],prime[maxn],tot,limit=5e6,sum[maxn],cnt;
unordered_map<int,int>mp;
void init(){
	int mx=5e6;tot=0;
	mu[1]=1;
	for(int i=2;i<=mx;i++){
		if(!vis[i])prime[++tot]=i,mu[i]=-1;
		vis[i]=1;
		for(int j=1;j<=tot&&i*prime[j]<=mx;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
	for(int j=1;j<=mx;j++)mu[j]+=mu[j-1];
}
void init2(){
	int mx=5e6;cnt=0;
	limit=mx;
	sum[1]=1;memset(vis,0,sizeof(vis));
	for(int i=2;i<=mx;i++){
		if(!vis[i])prime[++cnt]=i,sum[i]=i-1;
		vis[i]=1;
		for(int j=1;j<=tot&&i*prime[j]<=mx;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0){
				sum[i*prime[j]]=sum[i]*prime[j];
				break;
			}
			else sum[i*prime[j]]=sum[i]*(prime[j]-1);
		}
	}
	for(int i=2;i<=mx;i++)sum[i]+=sum[i-1];
}
int dfs(int x){
	if(x<=limit)return mu[x];
	if(mp.find(x)!=mp.end())return mp[x];
	int ans=1;
	for(int l=2,r;l<=x;l=r+1){
		r=x/(x/l);
		ans=ans-(r-l+1)*dfs(x/l);
	}
	mp[x]=ans;return ans;
}
int dfs1(int x){
	if(x<=limit)return sum[x];
	if(mp.find(x)!=mp.end())return mp[x];
	int ans=x*(x+1)/2;
	for(int l=2,r;l<=x;l=r+1){
		r=x/(x/l);
		ans=ans-(r-l+1)*dfs1(x/l);
	}
	mp[x]=ans;return ans;
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>t;
	init(),init2();
	while(t--){
		cin>>n;
		cout<<dfs1(n)<<" ";
		mp.clear();
		cout<<dfs(n)<<"\n";
		mp.clear();
	}
	return 0;
}

当然 杜教筛的常数是巨大的

在有些题目 你会发现它只要你求一个前缀和就够了 没有必要求出所有的前缀和

那么这时候 我们就可以选择换一种做法:Min_25筛

Min_25筛

在说明Min_25筛的具体做法之前 先说明其与杜教筛相比的好处与坏处:

好处:时间小 条件不那么严苛

坏处:只能求一个值 代码长度比杜教筛长

它解决如下问题:
\(\sum_{i=1}^{n}f(i)\) ,满足\(f(x)\)为积性函数 且\(f(p)\)在质数处容易计算/低阶多项式

那么如何求解?:(以luogu模板 Min_25筛为例)

Part 1 基础分类:

我们将数域分成质数和合数 即:
\(\sum_{1\leq q\leq n}f(p)+\sum_{1\leq p^e\leq n,1\leq p\leq \sqrt n}f(p^e)(\sum_{1\leq i\leq n/p^e 且 minp>p}f(i))\)

其中\(minp\)\(i\)的最小质因子大小

这样,整个式子就变成了两个部分,第一部分是所有质数的\(f\)之和,另一部分是枚举最小质因子后,求所有最小质因子大于这个质因子的\(f\)之和。

Part 2 初始处理

我们不妨设一个\(g(n,i)\)表示为在1到\(n\)中做\(i\)次筛法所剩下的数的贡献 (好吧人话就是要么是质数要么合数的最小质因子大于\(p_i\))

由于一个合数一定有一个质因子小于\(\sqrt n\)

那么我们就可以列出方程:

\[g(n,i)=g(n,i-1)-p_j^k(g(n/p_j^k,i-1)-g(p_{i-1},i-1)) \]

其中\(-g(p_{i-1},i-1)\)是因为在前面计算的时候把质数已经算进去了 而\(g(n/p_j^k,i-1)\)会使得其重复计算 于是减去

但是!这里有一个要注意的点 我们的\(p_j^k\)其实就是\(f'\) 即贡献(\(f'\)是什么先不用管

相当于我们可以把\(g\)写成若干个\(f'\)的性质 而他们相乘结果不变 这表明 \(f'\)必须要是一个完全积性函数

回到例题 我们发现所给\(f\)并不是完全积性函数 所以操作不合法 那如何去做?

我们发现 虽然\(p(p-1)\)不是积性函数 但是\(p^2\)\(p\)却是完全积性函数!所以我们拆开算就可以了

当然 你可以发现\(g(p_{i-1},i-1)\)是可以预处理的 因为它就等于质数个数

但是还有一个我们需要考虑的问题 :\(n\)的范围 由于\(n\)很大 所以不能直接转移

但是你又发现我们转移的时候都是通过\(n/p_j^k\)转移的 而在\(n\)范围内有效值就只有\(2\sqrt n\)个 可以用\(id\)预处理 具体而言 对于其小于\(\sqrt n\)的开一个数组 大于\(\sqrt n\)的也开一个数组 但是其下标为\(n/k\) 其中\(k\)为值

显然\(g\)可以循环枚举 于是压维计算

Part 3 计算答案:

类似的 我们定义\(S(n,i)\)\(g\)的定义一样 显然答案为\(S(n,0)\)

考虑转移:先定义\(sp_i\)为小于等于\(i\)的所有质数个数

\[S(n,i)=g(n)-sp_i(PS:质数贡献)+\sum_{p_k^e\leq n且 k>x}f(p_k^e)(S(\frac{n}{p_k^e},k)+[e!=1]) \]

\(e\)为1时 \(f(p_k^e)\)为质数贡献 而前面已经算过 所以不能计算

直接递归计算即可 好像不用记忆化 我也不知道为什么

一些小细节:

计算时不用把1计算入内 在最后使得答案加1即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int inv6=166666668;
int n,sqt;
int prime[maxn],vis[maxn],tot,sum1[maxn],sum2[maxn];//1次 2次
int w[maxn],id1[maxn],id2[maxn],cnt; 
int g1[maxn],g2[maxn];
void init(int x){
	for(int i=2;i<=x;i++){
		if(!vis[i]){
			prime[++tot]=i;
			sum1[tot]=(sum1[tot-1]+i)%mod;
			sum2[tot]=(sum2[tot-1]+i*i)%mod;
		}
		vis[i]=1;
		for(int j=1;j<=tot&&i*prime[j]<=x;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
}
int S(int x,int y){
	if(prime[y]>=x)return 0;
	int k=x<=sqt?id1[x]:id2[n/x];
	int ans=(((g2[k]-g1[k])-(sum2[y]-sum1[y]))%mod+mod)%mod;
	for(int i=y+1;i<=tot&&prime[i]*prime[i]<=x;i++){
		int pe=prime[i]; 
		for(int e=1;pe<=x;e++,pe*=prime[i]){
			int pos=pe%mod;
			ans=(ans+pos*(pos-1)%mod*(S(x/pe,i)+(e!=1))%mod)%mod;
			if(ans<0)ans+=mod;
		}
	}
	return ans%mod;
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	sqt=sqrt(n);
	init(sqt);
	for(int l=1;l<=n;){
		int r=n/(n/l);
		w[++cnt]=n/l;
		g1[cnt]=w[cnt]%mod;
		g2[cnt]=g1[cnt]*(g1[cnt]+1)%mod*(2*g1[cnt]+1)%mod*inv6%mod-1;
		g1[cnt]=g1[cnt]*(g1[cnt]+1)/2%mod-1;
		if(n/l<=sqt)id1[n/l]=cnt;
		else id2[n/(n/l)]=cnt;
		l=r+1;
	}
	for(int i=1;i<=tot;i++){
		for(int j=1;j<=cnt&&prime[i]*prime[i]<=w[j];j++){
			int k=(w[j]/prime[i]<=sqt?id1[w[j]/prime[i]]:id2[n/(w[j]/prime[i])]);\
			g1[j]=(g1[j]-prime[i]*(g1[k]-sum1[i-1]+mod)%mod)%mod;
			g2[j]=(g2[j]-prime[i]*prime[i]%mod*(g2[k]-sum2[i-1])%mod)%mod;
			if(g1[j]<0)g1[j]+=mod;
			if(g2[j]<0)g2[j]+=mod; 
		}
	}
	cout<<(S(n,0)+1)%mod<<"\n";
	return 0;
}

后记:

用了大概半个上午左右 反正数论吗,多总结总没有错

有挺多东西是挺模糊的 写了一遍清晰了很多 当然肯定还有问题

最近就学了这么多 以后学到了新的再加

posted on 2023-05-13 14:55  C2023CYJ  阅读(54)  评论(0)    收藏  举报