数论\to 模范

本文未经说明全是整数,全是整除。

aifoson 括号

[1]=1,[1=0]=0,[[1=0]=0]=1.

diliclate 卷积

符号

fg 表示两个数论函数的 dlk 卷积。

一般卷积的形式:

(fg)(x)=i=f(i)g(xi)

因为 2.,所以枚举的值是要整除 x 的才能保证,数论卷积式子:

(fg)(x)=i|xf(i)g(xi)

  1. dlk 卷积满足交换律,结合律

1.易证,因为所有卷积的形式都是对称的。

是u论函数

  1. 值域,定义域 Z

常见数论函数

I(x):恒等函数,函数值恒为 1
ϵ(x)/e(x):单位元函数,e(x)=[x=1],有性质 ef=f
id(x):单位函数,id(x)=x
idn(x):幂函数,idn(x)=xn
以上都是完全积性函数。

  1. 完全积性函数满足 f(ab)=f(a)f(b)

φ(n) 表示 [1,n] 中与 n 互质的数的个数(φ(n)=μ(n)id(n))。
μ(n) 表示 ,但是看不懂。
以上都是积性函数。

  1. 积性函数满足 (a,b)=1f(ab)=f(a)f(b)

积性函数 f 的一些性质

  1. f(1)=1,由 f(1)=f(1)f(1) 可得。
  2. f(x)=f(p1k1)f(p2k2)f(pmkm),由各质因子互质可得。
  3. 两个积性函数的 dlk 卷积还是积性函数。
    证明:
    🐍 f1,f2 为积性函数,(a,b)=1,记 g(x)=f1(x)f2(x)=d|xf1(d)f2(xd)

    g(a)g(b)=d|af1(d)f2(ad)t|bf1(t)f2(bt)=d|at|bf1(d)f2(ad)f1(t)f2(bt)

    由于 a,b 互质,所以 d,t 互质,两个求和可以合并:

    g(a)g(b)=dt|abf1(dt)f2(abdt)=g(ab)

  4. 若存在 gf=e,则称 gf,且 g 也是积性函数,这可以顺便证明 μ(n) 是积性函数。

μ 的推导

以下默认函数为积性函数。

设两单变量函数 F,f 满足 F(n)=d|nf(d)
那么有 F(n)=d|nI(d)f(d)=I(n)f(n)
变形一下 f=I1F,于是我们只需要求出 I1 即可。
大佬Möbius把 I1 命名为 μ
这里有个小技巧,研究一个积性函数,先研究其在质数的幂时的表现。
先看 μ(pk) 的值。

  • k=0,由 5.,μ(1)=1
  • k=1μ(p)I(p)=d|pμ(d)I(pd)=μ(1)I(p)+μ(p)I(1)=1+μ(p)=e(p)=0,所以 μ(p)=1
  • k>1μ(pk)I(pk)=d|pkμ(d)I(pkd)=e(pk)=0,消去 I(因为 I 都等于 1),d|pkμ(d)=μ(1)+μ(p)+μ(p2)++μ(pk)=0
    再用数学归纳法不难得到 μ(pk)=0

根据 4.,对于 n=p1k1p2k2psksμ(n)=μ(p1k1)μ(p2k2)μ(psks)
又因为 k>1μ(pk)=0,所以当 i,ki>1 时,μ(n)=0,否则 μ(n)=(1)s
这下就好理解 了。

莫反公式

A. 嵌入式莫比乌斯反演

  1. [n|m]d|nmμ(d)=[n=m]

证明:
有一个显然的东西:[n|m][nm=1]=[n=m],因为一个数整除另一个数又除另一个数等于 1(即 0),这两个数肯定相等。
然后再看这个式子:

μI=e

展开:

d|nμ(d)I(nd)=e(n)

d|nμ(d)=e(n)

n=nm 代入得

d|nmμ(d)=e(nm)=[nm=1]=[n=m][n|m]()

[n|m]d|nmμ(d)=[n=m]

从中间可以得到的副产品

  1. d|nμ(d)=[n=1]

但是是最有用的式子。

B. 约数的莫比乌斯反演

  1. F(n)=d|nf(d),则 f(n)=d|nμ(d)F(nd)

证明:
F 的定义,有 F=If,即 f=μF,所以

f(n)=μ(n)F(n)=d|nμ(d)F(nd)=d|nF(d)μ(nd)

C. 倍数的莫比乌斯反演

  1. F(n)=n|df(d),则 f(n)=n|dμ(dn)F(d)

ben 说做题基本不会用,待补。

一些题目

线性筛会了就可以开始做题了,筛质数的过程中可以顺便按照其定义处理 μφ

GCD

T 组询问,求

x=1ny=1m[gcd(x,y)=1]

1n,m5×104

n<m

由 10. 得

x=1ny=1m[gcd(x,y)=1]=x=1ny=1md|gcd(x,y)d|x,d|yμ(d)

考虑 d,x,y 的取值范围都为 [1,nm],可以把 d 移到前面去

x=1ny=1m[gcd(x,y)=1]=d=1nμ(d)x|nn1y|mm1=d=1nμ(d)x|nn1y|mm1=d=1nμ(d)ndmd

到这里就可以 O(n) 算了,但是会 TLE。

我们需要一个新东西,整除分块

然后就可以做到 O(n) 算了,总时间复杂度 O(Tn)

P4450 双亲数

x=1ny=1m[gcd(x,y)=t]

1tn,m106

题目存在一个隐含的条件,对于能计数的情况,一定有 t|x,t|y,所以

x=1ny=1m[gcd(x,y)=t]=t|xnt|ym[gcd(x,y)=t]

两边除以 t

x=1ny=1m[gcd(x,y)=t]=1|xx=1nt1|yy=1mt[gcd(x,y)=1]

然后就是上题的板子了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int pr[80003],cnt,n,m,d,ans;
bool isp[1000003];
int mu[1000003];
int premu[1000003];
void sieve(){
	premu[1]=mu[1]=1;
	for(int i=2;i<=n;i++){
		if(!isp[i])	pr[++cnt]=i,mu[i]=-1;
		for(int j=1;i*pr[j]<=n;j++){
			isp[i*pr[j]]=1;
			mu[i*pr[j]]=(i%pr[j]?-mu[i]:0);
			if(i%pr[j]==0) break;
		}
		premu[i]=premu[i-1]+mu[i];
	}
}
signed main(){
	cin>>n>>m>>d;
	if(n<m) swap(n,m);
	sieve();
	for(int i=1;i<=m/d;i++)
		ans+=mu[i]*(n/d/i)*(m/d/i);
	cout<<ans;
	return 0;
}

P3455 [POI2007] ZAP-Queries

上题带多组询问。

加个数论分块即可,时间复杂度 O(Tn)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int pr[80003],cnt,n,m,d,ans;
bool isp[1000003];
int mu[1000003];
int premu[1000003];
void sieve(){
	premu[1]=mu[1]=1;
	for(int i=2;i<=50000;i++){
		if(!isp[i])	pr[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*pr[j]<=50000;j++){
			isp[i*pr[j]]=1;
			mu[i*pr[j]]=(i%pr[j]?-mu[i]:0);
			if(i%pr[j]==0) break;
		}
		premu[i]=premu[i-1]+mu[i];
	}
}
void solve(){
	cin>>n>>m>>d;
	if(n<m) swap(n,m);
	ans=0; n/=d; m/=d;
	for(int l=1,r=0;l<=m;l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=(premu[r]-premu[l-1])*(n/l)*(m/l);
	}
	cout<<ans<<'\n';
}
int T;
signed main(){
	cin>>T; sieve();
	while(T--) solve();
	return 0;
}

P2522 [HAOI2011] Problem b

T 组询问,求

x=aby=cd[gcd(x,y)=k]

1T,a,b,c,d,k5×104

可以用二维差分,则答案为 solve(b,d)solve(a1,d)solve(b,c1)+solve(a1,c1),时间复杂度 O(Tn)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int pr[80003],cnt,ans;
bool isp[1000003];
int mu[1000003];
int premu[1000003];
void sieve(){
	premu[1]=mu[1]=1;
	for(int i=2;i<=50000;i++){
		if(!isp[i])	pr[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*pr[j]<=50000;j++){
			isp[i*pr[j]]=1;
			mu[i*pr[j]]=(i%pr[j]?-mu[i]:0);
			if(i%pr[j]==0) break;
		}
		premu[i]=premu[i-1]+mu[i];
	}
}
int solve(int n,int m,int d){
	if(n<m) swap(n,m);
	ans=0; n/=d; m/=d;
	for(int l=1,r=0;l<=m;l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=(premu[r]-premu[l-1])*(n/l)*(m/l);
	}
	return ans;
}
int T,a,b,c,d,k;
signed main(){
	cin>>T; sieve();
	while(T--){
		cin>>a>>b>>c>>d>>k;
		cout<<solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k)<<'\n';
	}
	return 0;
}

P6156 简单题 / 加强版 P6222

題面為加强版。

給出 kT 組詢問给你 n 求下式的值:

i=1nj=1n(i+j)kμ2(gcd(i,j))gcd(i,j)

uint 自然溢出。

对于 100% 的数据,1n1071k231

P4240 毒瘤之神的考验

n,m,求

i=1nj=1mφ(ij)

对 998244353 取模,多测。

n,m105,T104

拆式子与根号分治思想的极致融合。

nm

先要知道一个经典的式子:

φ(ij)=φ(i)φ(j)gcd(i,j)φ(gcd(i,j))

然后拆式子

i=1nj=1mφ(ij)=i=1nj=1mφ(i)φ(j)gcd(i,j)φ(gcd(i,j))

枚举 d=gcd(i,j)

i=1nj=1mφ(i)φ(j)gcd(i,j)φ(gcd(i,j))=d=1ni=1nj=1mφ(i)φ(j)d[gcd(i,j)=d]φ(d)=d=1ndφ(d)i=1nj=1mφ(i)φ(j)[gcd(i,j)=d]=d=1ndφ(d)i=1n/dj=1m/dφ(id)φ(jd)[gcd(i,j)=1]

后面那个直接莫反拆掉,把枚举扔外面

d=1ndφ(d)i=1n/dj=1m/dφ(id)φ(jd)[gcd(i,j)=1]=d=1ndφ(d)i=1n/dj=1m/dφ(id)φ(jd)ti,tjμ(t)=d=1ndφ(d)t=1n/dμ(t)i=1n/dj=1m/dφ(idt)φ(jdt)=t=1ndtdμ(td)φ(d)i=1n/tφ(it)j=1m/tφ(jt)

这个式子就很好看了,设

f(n)=d|ndμ(nd)φ(d),g(n,k)=i=1nφ(ik)

则原式为

t=1nf(t)g(n/t,t)g(m/t,t)

f,g 直接预处理是可以的,因为 g 的数量是 nlogn 级别的,且 g 可以直接递推:

g(n,k)=g(n1,k)+φ(nk)

所以预处理 f,g 时间复杂度 O(nlogn)

由于原式并不好整除分块,也不能暴力预处理全部信息,所以我们只能考虑分治。

设阈值为 B,记原式为三元函数 h(a,b,n)=t=1nf(t)g(a,t)g(b,t),考虑当 a,bB 时直接暴力预处理答案,时空复杂度 O(nB2),这个三元函数就可以数论分块了,即

n/l=m/l,n/r=m/rh(n/l,m/l,r)h(n/l,m/l,l1)

h 也可以递推

h(a,b,n)=h(a,b,n1)+f(n)g(a,n)g(b,n)

否则可以得到 n/aB,an/B,暴力统计答案,时间复杂度 O(n/B)

综上,时间复杂度 O(nlogn+nB2+T(n+nB)),取 B=T3=22,可以通过,500~700 ms,空间复杂度 O(nlogn+nB2)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int maxn=1e5+7;
const int B=24;
const int maxb=B+2;
const int N=1e5;
const int mod=998244353;
bool st;
int mu[maxn],phi[maxn],pr[maxn],pcnt;
ll f[maxn],inv[maxn];
bool isp[maxn];
vector<ll>g[maxn],h[maxb][maxb];
bool ed;
void solve(){
    int n,m,l=1,r;
    cin>>n>>m;
    if(n>m) swap(n,m);
    ll ans=0;
    for(;l<=m/B;l++)
        ans=(ans+f[l]*g[l][n/l]%mod*g[l][m/l]%mod)%mod;
    for(;l<=n;l=r+1){
        r=min(n/(n/l),m/(m/l));
        ans=(ans+h[n/l][m/l][r]-h[n/l][m/l][l-1]+mod)%mod;
    }
    cout<<ans<<'\n';
}
signed main(){
    cerr<<(&ed-&st)/1048576.0<<" MB\n";
    inv[1]=mu[1]=phi[1]=1;
    for(int i=2;i<=N;i++){
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        if(!isp[i]) pr[++pcnt]=i,mu[i]=-1,phi[i]=i-1;
        for(int j=1;j<=pcnt&&i*pr[j]<=N;j++){
            isp[i*pr[j]]=1;
            if(i%pr[j]==0){
                mu[i*pr[j]]=0;
                phi[i*pr[j]]=pr[j]*phi[i];
                break;
            }
            mu[i*pr[j]]=-mu[i];
            phi[i*pr[j]]=phi[i]*phi[pr[j]];
        }
    }
    for(int i=1;i<=N;i++){
        g[i].resize(N/i+2); g[i][0]=0;
        for(int j=1;j<=N/i;j++)
            g[i][j]=(g[i][j-1]+phi[i*j])%mod;
    }
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N/i;j++)
            f[j*i]=(f[j*i]+i*mu[j]*inv[phi[i]]%mod+mod)%mod;
    for(int j=1;j<=B;j++){
        for(int k=1;k<=B;k++){
            h[j][k].resize(N/k+7); h[j][k][0]=0;
            for(int i=1;i<=N/k;i++)
                h[j][k][i]=(h[j][k][i-1]+f[i]*g[i][j]%mod*g[i][k]%mod)%mod;
        }
    }
    int TEST;
    cin>>TEST;
    while(TEST--){
        solve();
    }
    return 0;
}
posted @   view3937  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
Title
点击右上角即可分享
微信分享提示