数论其二
1.数论函数的定义:
指定义域是正整数的函数。
2.积性函数与完全积性函数:
积性函数:若\(a,b\)互质,且\(f(ab)=f(a)f(b)\),那么函数\(f(x)\)是积性函数
完全积性函数:\(\forall a,b\),有\(f(ab)=f(a)f(b)\),那么函数\(f(x)\)是完全积性函数
3.常用的数论函数
\(id_k(n)=n^k,\epsilon(n)=[n==1],\sigma_k(n)=\sum_{d|n}d^k\)
4.欧拉函数:
定义:
\(1\sim n\)中与 \(n\) 互质的数的个数是欧拉函数,记作\(\phi(n)\)。
计算式:
若$n=p_1^{c_1}\times p_2^{c_2}\times ...\times p_m^{c_m} $ (\(p_1,p_2...p_m\) 为质数),则:
性质:
①:欧拉函数是积性函数,即若\(a,b\)互质,则\(\phi(ab)=\phi(a)\phi(b)\)
②:\(1\sim n\)中与 \(n\) 互质的数的和是 \(\frac{\space n\phi(n)}{2}\)
③:如果 \(p\) 是质数,\(p|n\) 且 \(p^2 |n\),则\(\phi(n)=\phi(n/p)\times p\)
④:如果 \(p\) 是质数,\(p|n\) 且 \(p^2 |n\),则\(\phi(n)=\phi(n/p)\times (p-1)\)
⑤:\(\sum_{d|n}\phi(d)=n\),用卷积表示是\(\phi*I=id\),即欧拉反演。
5.欧拉定理和扩展欧拉定理:
欧拉定理:若正整数 \(a,n\) 互质,则\(a^{\phi(n)}\equiv 1(mod\space n)\)
扩展欧拉定理:
若正整数 \(a,n\) 互质,则对于任意正整数 \(b\),有
对于不互质的情况,有
P5091 【模板】扩展欧拉定理
while(b=getchar())
if(!(b<'0'||b>'9'))
break;
while(b>='0'&&b<='9'){//处理非常大的数的技巧
num=num*10+b-'0';
if(num>=phi)
num=num%phi,mod=1;//phi表示phi(m)
b=getchar();
}
if(mod) num+=phi;
printf("%lld\n",fpow(a,num));//fpow为快速幂
P4139 上帝与集合的正确用法
直接应用扩展欧拉定理,然后递归暴算,直到模数为 \(1\) 停止。
long long work(long long mod){
if(mod==1)
return 0;
return f(2,work(phi[mod])+phi[mod],mod);//f为快速幂
}
//主函数中:
while(t--){
scanf("%d",&n);
printf("%lld\n",work(n));
}
P2480 [SDOI2010] 古代猪文
一句活题意:求
如果\(g=999911659\),那么答案是 \(0\) 。
否则,因为 \(9999111659\) 是质数,所以 \(g\) 与 \(999911659\) 互质。根据欧拉定理,有:
关键就是求\(\sum_{d|n}C_n^d(mod\space 999911658)\)。直接求比较困难,因为模数太大,无法应用卢卡斯定理降低复杂度。
考虑将 \(999911658\) 质因数分解,得到 \(2\times 3\times 4679\times 35617\) 。
可以暴力枚举 \(n\) 的因数,然后用卢卡斯定理分别求出 \(\sum_{d|n}C_n^d\) 对 \(2,3,4679,35617\) 的结果,记为 \(a_1,a_2,a_3,a_4\) ,然后求同余方程组:
就能得到\(\sum_{d|n}C_n^d(mod\space 999911658)\),然后快速幂算出\(ans\)即可。
\(code:\)
signed main(){
scanf("%lld%lld",&n,&g);
if(g==999911659){//别忘了特判
printf("0\n");
return 0;
}
for(int i=1;i<=4;++i)
pre(p[i],i);//预处理阶乘和阶乘逆元
for(int i=1;i*i<=n;++i){
if(n%i==0){
for(int j=1;j<=4;++j)
a[j]=(a[j]+dfs(n,i,p[j],j))%p[j];//卢卡斯定理
if(i*i!=n){
for(int j=1;j<=4;++j)
a[j]=(a[j]+dfs(n,n/i,p[j],j))%p[j];
}
}
}
for(int i=1;i<=4;++i){
int tmp;
exgcd(sum/p[i],p[i],tmp,y);
tmp=tmp*a[i]%p[i];
tmp=tmp*(sum/p[i]);
x+=tmp;
}
x=(x+sum)%sum;
printf("%lld\n",fpow(g,x,mod));
return 0;
}
P3747 [六省联考 2017] 相逢是问候
回忆一下《上帝与集合的正确用法》,可知:对于一个数,使用题目中的操作,操作一定次数后就会变为一个固定的数。可以证明,操作次数是对数级别的。
所以,类似于区间开方,修改时先暴力修改,直到变成固定的数后,就打上标记,以后不再修改它。
首先设 \(a[i][j]\) 表示第 \(i\) 个数进行 \(j\) 次操作后的值,然后预处理出所有的 \(a[i][j]\) 。
接下来考虑线段树。线段树除了维护区间和以外,还要维护一个 \(id\) 。\(id\) 表示该节点代表的区间所有的数都已经操作了 \(j\) 次。特别地, \(id=-1\) 表示该节点代表的区间所有的数的 \(id\) 值不完全相同。
然而,如果直接套用《上帝与集合的正确用法》中的方法处理 \(a[i][j]\) ,复杂度是\(O(nlog^3n)\),会超时。可以对快速幂进行优化,将复杂度优化到\(O(nlog^2n)\)。具体而言,先预处理出 \(fpow1[i](i\in[1,10000])\) 表示 \(c^{i}\) 和\(fpow2[i]\)表示 \(c^{10000i}\) 。假设我们要计算 \(c^k\) ,那么我们只需要算出 \(x=k/10000,y=k\%10000\) ,就有\(c^k=fpow1[y]\times fpow2[x]\)
\(code:\)
void pre_work(int now){
++cnt;
phi[cnt]=now;int tmp=now;
for(int i=2;i*i<=now;++i)
if(tmp%i==0){
while(tmp!=1&&tmp%i==0)
tmp/=i;
phi[cnt]=phi[cnt]/i*(i-1);
}
if(tmp!=1)
phi[cnt]=phi[cnt]/tmp*(tmp-1);
if(phi[cnt]==1)
return ;
pre_work(phi[cnt]);
}
int fpow(int x,int y,int mod){
int s=x;x=1;bool ok=0;
while(y){
if(y&1) x=x*s;
y>>=1;s=s*s;
if(s>=mod&&y) ok=1,s%=mod;
if(x>=mod) ok=1,x%=mod;
}
if(x>=mod) ok=1;
return ok?x%mod+mod:x%mod;
}
void pre_work2(){
for(int j=1;j<=cnt;++j){
fpow1[0][j]=fpow2[0][j]=1;
int tmp=fpow(c,10000,phi[j]);
bool ok1=0,ok2=0;
if(c>=2) ok2=1;
for(int i=1;i<=10000;++i){
fpow1[i][j]=fpow1[i-1][j]*c;
if(fpow1[i][j]>=phi[j]||ok1)
fpow1[i][j]=fpow1[i][j]%phi[j]+phi[j],ok1=1;
fpow2[i][j]=fpow2[i-1][j]*tmp;
if(fpow2[i][j]>=phi[j]||ok2)
fpow2[i][j]=fpow2[i][j]%phi[j]+phi[j],ok2=1;
}
}
}
int work(int w,int now,int cnt){
if(now==cnt+1){
if(w>=phi[now])
return w%phi[now]+phi[now];
return w%phi[now];
}
int tmp=work(w,now+1,cnt),x,y;
x=tmp/10000;y=tmp%10000;
x=fpow2[x][now]*fpow1[y][now];
if(x>=phi[now])
x=x%phi[now]+phi[now];
return x;
}
void push_up(int u){
p[u].w=(p[u<<1].w+p[u<<1|1].w)%mod;
if(p[u<<1].id!=-1&&p[u<<1].id==p[u<<1|1].id)
p[u].id=p[u<<1].id;
else p[u].id=-1;
}
void build(int u,int l,int r){
p[u].l=l;p[u].r=r;
if(l==r){
p[u].w=a[l][0];
return ;
}
int mid=(l+r)>>1;
build(u<<1,l,mid);build(u<<1|1,mid+1,r);
push_up(u);
}
void add(int u,int l,int r,int w){
if(p[u].id==cnt-1)
return ;
if(p[u].l==p[u].r){
p[u].id+=w;
p[u].w=a[p[u].l][p[u].id];
return ;
}
int mid=(p[u].l+p[u].r)>>1;
if(mid>=l)
add(u<<1,l,r,w);
if(mid<r)
add(u<<1|1,l,r,w);
push_up(u);
}
int ask(int u,int l,int r){
if(p[u].l>=l&&p[u].r<=r)
return p[u].w;
int mid=(p[u].l+p[u].r)>>1,re=0;
if(mid>=l)
re=(re+ask(u<<1,l,r))%mod;
if(mid<r)
re=(re+ask(u<<1|1,l,r))%mod;
return re;
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&m,&mod,&c);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i][0]);
phi[cnt=1]=mod;
pre_work(mod);
phi[++cnt]=1;
pre_work2();
for(int i=1;i<=n;++i)
for(int j=1;j<cnt;++j)
a[i][j]=work(a[i][0],1,j)%mod;
build(1,1,n);
for(int i=1;i<=m;++i){
scanf("%lld%lld%lld",&opt,&l,&r);
if(opt==0)
add(1,l,r,1);
else
printf("%lld\n",ask(1,l,r));
}
return 0;
}
5.莫比乌斯函数:
定义:
莫比乌斯反演:
形式0(因数形式):设 \(F(n)=\sum_{d|n}f(d)\),即 \(F=I*f\) ,则 \(f=\mu*F\)
形式1(倍数形式):设 \(F(n)=\sum_{n|d}f(d)\) ,则 \(f(n)=\sum_{n|d}\mu(\frac{d}{n})f(d)\)
线性求欧拉函数和莫比乌斯函数:
for(int i=2;i<=1e7;++i){
if(!a[i])
a[i]=i,p[++tot]=i;
for(int j=1;j<=tot;++j){
if(p[j]*i>1e7||p[j]>a[i])
break;
a[p[j]*i]=p[j];
}
}
phi[1]=miu[1]=1;
for(int i=2;i<=1e7;++i){
int tmp=i/a[i];
if(tmp%a[i]==0){
phi[i]=phi[tmp]*a[i];
miu[i]=0;
}
else{
phi[i]=phi[tmp]*(a[i]-1);
miu[i]=miu[tmp]*-1;
}
}
P1390公约数的和
简单莫反题。要求
可以先考虑问题的简化版:
然后用一个数论分块,就可以求出该柿子。
再考虑题目中要求我们算的柿子,设它为\(ans\)。
不难发现\(\sum_{i=1}^{n}\sum_{j=1}^ngcd(i,j)=ans\times 2+\sum_{i=1}^{n}gcd(i,i)=ans\times 2+\frac{n\times (n+1)}{2}\)
然后就能愉快地求出来了。
P1447能量采集
不难发现,题目让我们求的是\(\sum_{i=1}^{n}\sum_{j=1}^{m}2\times gcd(i,j)-n\times m\)
于是就转化成为了上一题。\(ans=2\times \sum_{d=1}^nd\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\mu(i)\lfloor \frac{n}{di}\rfloor\lfloor \frac{m}{di}\rfloor -n\times m\)(这里假定\(n>=m\))
YY的GCD
莫反\(+\)一个小优化。先假定\(n>=m\)。仍是按照第一题的方式推柿子,\(ans=\sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\mu(i)\lfloor \frac{n}{di}\rfloor\lfloor \frac{m}{di}\rfloor(d\in prime)\)
然而这样复杂度是\(O(n\space logn)\)的,仍会超时,肿么办?
注意到,数对 \((i,d)\) 相当于枚举了所有乘积小于等于 \(n\) 的数(且后一个数是质数)。令 \(T=i\times d\),考虑枚举每一个 \(T\) 对答案有多少贡献(这是一个套路,后面会经常用):
然后发现, \(\sum_{k|T,k\in prime}^{} \mu( \frac{T}{k})\) 是可以预处理的。枚举每一个质数 \(p\) ,然后令它的倍数 \(k\) 加上 \(\mu(\frac{k}{p})\) 。
\(code:\)
void work(){
ll r=0,a=n,b=m;
if(a<b) swap(a,b);
for(int i=1;i<=b;i=r+1){
r=min(a/(a/i),b/(b/i));
if(r>b) r=b;
ans+=(sum[r]-sum[i-1])*(a/i)*(b/i);//注意是a/i下取整,要加括号
}
}
int main(){
for(int i=1;i<=maxn;++i)
miu[i]=1;
for(int i=2;i<=maxn;++i){
if(!vis[i]){
p[++tot]=i,miu[i]=-1;
for(int j=i*2;j<=maxn;j+=i){
vis[j]=1;
if(j/i%i==0) miu[j]=0;
else miu[j]*=-1;
}
}
}
for(int i=1;i<=maxn;++i)
for(int j=1;p[j]*i<=maxn&&j<=tot;++j)
s[i*p[j]]+=miu[i];
for(int i=1;i<=maxn;++i)
sum[i]=sum[i-1]+s[i];
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
ans=0;
work();
printf("%lld\n",ans);
}
return 0;
}
P3327约数个数和
前言:一些常用的二级结论:
推柿子参看题解区
然后令\(F(n)=\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor\),则
然后用数论分块即可。时间复杂度 \(O(n\space \sqrt n+T\space \sqrt n)\) 。
\(code;\)
void work(){
scanf("%lld%lld",&n,&m);
if(n<m)
swap(n,m);
int ans=0;
for(int i=1,r=0,rr=0;i<=m;i=r+1){
r=n/(n/i);rr=m/(m/i);//cout<<r<<" "<<rr<<endl;
r=min(r,min(rr,m));//cout<<m/i<<" "<<m/r<<" "<<n/i<<" "<<n/r<<endl;
ans+=(miu[r]-miu[i-1])*s[m/i]*s[n/i];
}
printf("%lld\n",ans);
}
signed main(){
scanf("%lld",&t);
for(int i=2;i<=100000;++i){
if(a[i]==0)
p[++tot]=i,a[i]=i;
for(int j=1;j<=tot;++j){
if(i*p[j]>100000||a[i]<p[j])
break;
a[i*p[j]]=p[j];
}
}
for(int i=1;i<=100000;++i)
miu[i]=1;
for(int i=2;i<=100000;++i){
int tmp=i/a[i];
if(tmp%a[i]==0)
miu[i]=0;
else
miu[i]=-miu[tmp];
}
for(int i=1;i<=100000;++i)
miu[i]+=miu[i-1];
for(int i=1;i<=100000;++i){
for(int j=1,r=0;j<=i;j=r+1){
r=i/(i/j);
s[i]+=(r-j+1)*(i/j);
}
}
while(t--)
work();
//for(int i=1;i<=100;++i)cout<<miu[i]<<" ";cout<<endl;
return 0;
}
P3768简单的数学题
利用 \(\phi*I=id\) 可以得到:
其中 \(F(n)=n\times (n+1)/2\)
其中 \(\sum_{d=1}^{n}F^2(\lfloor \frac{n}{d}\rfloor)\) 可以用数论分块处理,剩下的部分考虑杜教筛。
剩下的部分是 \(d^2\phi(d)\) ,设 \(\sum_{d=1}^{n}d^2\phi(d)\) 为 \(s(n)\) ,考虑构造函数 \(g(n)=n^2\) ,则有:
化简得到:
P3312数表
先不考虑 \(a\) 的限制,有:
(其中 \(f(x)\) 表示 \(x\) 的因数和,可以预处理得到)
接下来考虑 \(a\) 。首先将询问按照 \(a\) 从小到大排序,然后开一个树状数组(其中单点\(i\)表示 \((f*\mu)(i)\) ),每次遇到可以加入树状数组的 \(f(i)\) ,就枚举 \(i\) 的倍数,令 \(ki\) 的单点数值增加 \(f(i)\times\mu(k)\) 。然后在数论分块的时候,如果当前区间为 \([l,r]\) ,就让 \(ans\) 乘以 \([l,r]\) 的区间和。
\(code:\)
bool cmp(node a,node b){
return a.a<b.a;
}
bool cmp2(node a,node b){
return a.w<b.w;
}
void add(long long x,long long w){
while(x<=L)
c[x]=(c[x]+w)%mod,x+=x&(-x);
}
long long ask(long long x){
long long re=0;
while(x)
re=(re+c[x])%mod,x-=x&(-x);
return re;
}
long long work(long long n,long long m){
int ans=0;
if(n<m)
swap(n,m);
for(int i=1,r=0;i<=m;i=r+1){
r=min(n/(n/i),min(m/(m/i),m));
ans=(ans+((n/i)%mod*(m/i)%mod*((ask(r)-ask(i-1)+mod)%mod)%mod))%mod;
}
return ans;
}
void pre_work(){
for(int i=2;i<=L;++i){
if(a[i]==0)
a[i]=i,prime[++tot]=i;
for(int j=1;j<=tot;++j){
if(prime[j]*i>L||prime[j]>a[i])
break;
a[i*prime[j]]=prime[j];
}
}
miu[1]=1;
for(int i=2;i<=L;++i){
int j=i/a[i];
if(j%a[i]==0)
miu[i]=0;
else
miu[i]=miu[j]*(-1);
}
for(int i=1;i<=tot;++i){
long long now=prime[i];
p[now][0]=sum[now][0]=1;
long long tmp=0;
while(sum[now][tmp]<=L){
++tmp;
p[now][tmp]=p[now][tmp-1]*prime[i];
sum[now][tmp]=(sum[now][tmp-1]+p[now][tmp]);
}
}
for(int i=1;i<=L;++i){
long long tmp=i,cnt=0;
f[i].w=1;f[i].id=i;
for(int j=1;j<=tot&&prime[j]*prime[j]<=i;++j){
cnt=0;
while(tmp%prime[j]==0)
tmp/=prime[j],++cnt;
f[i].w=f[i].w*sum[prime[j]][cnt];
}
if(tmp!=1)
f[i].w=f[i].w*sum[tmp][1];
}
sort(f+1,f+L+1,cmp2);
}
signed main(){
pre_work();
scanf("%lld",&t);
for(int i=1;i<=t;++i)
scanf("%lld%lld%lld",&v[i].n,&v[i].m,&v[i].a),v[i].id=i;
sort(v+1,v+t+1,cmp);
for(int i=1,j=1;i<=t;++i){
while(j<=L&&f[j].w<=v[i].a){
for(int k=f[j].id;k<=L;k+=f[j].id)
add(k,f[j].w*miu[k/f[j].id]%mod);
++j;
}
ans[v[i].id]=work(v[i].n,v[i].m);
}
for(int i=1;i<=t;++i)
printf("%lld\n",ans[i]);
return 0;
}
P1891疯狂LCM
将 \(lcm(i,n)\) 用 \(n\times i/gcd(n,i)\) 表示,于是原式化为:
引理:\(\sum_{i=1}^ni[gcd(i,n)==1]=\frac{\phi(n)\times n}{2}\)
证明:左边的柿子表示\([1,n]\)中与\(n\)互质的数的和,而\([1,n]\)中与\(n\)互质的数是成对出现的,每一对的和为 \(n\) ,所以与 \(n\) 互质的数的平均值为 \(\frac{n}{2}\) ,和为 \(\frac{\phi(n)\times n}{2}\)
因此,上述柿子就化为:
令 \(f(n)=\sum_{d|n}\frac{\phi(d)\times d}{2}\) ,则 \(f(n)\) 可以预处理得到。外层的 \(i\) 循环到 \(n\) ,内层的 \(j\) 循环到 \(\lfloor \frac{n}{i}\rfloor\) ,每次令 \(f(i\times j)+=\frac{\phi(i)\times i}{2}\) 即可。
P4449 于神之怒加强版
其中,\(\sum_{d|T}d^k\mu(\frac{T}{d})\) 预处理即可。
P3704 [SDOI2017] 数字表格
其中,\(\prod_{d|T}f_{d}^{\mu(\frac{T}{d})}\) 预处理即可。

浙公网安备 33010602011771号