数论其二
1.数论函数的定义:
指定义域是正整数的函数。
2.积性函数与完全积性函数:
积性函数:若
完全积性函数:
3.常用的数论函数
4.欧拉函数:
定义:
计算式:
若
性质:
①:欧拉函数是积性函数,即若
②:
③:如果
④:如果
⑤:
5.欧拉定理和扩展欧拉定理:
欧拉定理:若正整数
扩展欧拉定理:
若正整数
对于不互质的情况,有
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 上帝与集合的正确用法
直接应用扩展欧拉定理,然后递归暴算,直到模数为
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] 古代猪文
一句活题意:求
如果
否则,因为
关键就是求
考虑将
可以暴力枚举
就能得到
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] 相逢是问候
回忆一下《上帝与集合的正确用法》,可知:对于一个数,使用题目中的操作,操作一定次数后就会变为一个固定的数。可以证明,操作次数是对数级别的。
所以,类似于区间开方,修改时先暴力修改,直到变成固定的数后,就打上标记,以后不再修改它。
首先设
接下来考虑线段树。线段树除了维护区间和以外,还要维护一个
然而,如果直接套用《上帝与集合的正确用法》中的方法处理
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(因数形式):设
形式1(倍数形式):设
线性求欧拉函数和莫比乌斯函数:
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公约数的和
简单莫反题。要求
可以先考虑问题的简化版:
然后用一个数论分块,就可以求出该柿子。
再考虑题目中要求我们算的柿子,设它为
不难发现
然后就能愉快地求出来了。
P1447能量采集
不难发现,题目让我们求的是
于是就转化成为了上一题。
YY的GCD
莫反
然而这样复杂度是
注意到,数对
然后发现,
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约数个数和
前言:一些常用的二级结论:
推柿子参看题解区
然后令
然后用数论分块即可。时间复杂度
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简单的数学题
利用
其中
其中
剩下的部分是
化简得到:
P3312数表
先不考虑
(其中
接下来考虑
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
将
引理:
证明:左边的柿子表示
因此,上述柿子就化为:
令
P4449 于神之怒加强版
其中,
P3704 [SDOI2017] 数字表格
其中,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】