莫比乌斯反演 习题
[POI2007] ZAP-Queries
(题目传送门)
题意
给出
解题思路
-
原题要求的是
-
将
除以 得 -
由莫比乌斯反演得
-
再将
除以 ,这样就不关心 是否为 的约数,可以直接枚举 : -
这个式子的后半部分可以整除分块来优化,前半部分预处理前缀和即可。在代码实现时,可以预先让
同除以 ,方便实现
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=50010;
int T,a,b,d,n,m;
int prime[N],v[N],tot,mu[N],sum[N];
LL ans;
void primes()
{
mu[1]=1;
for(int i=2; i<=50000; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>50000/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1; i<=50000; i++)
sum[i]=sum[i-1]+mu[i];
}
int main()
{
primes();
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&a,&b,&d);
n=a/d; m=b/d;
if(n>m)
swap(n,m);
ans=0;
for(int l=1,r; l<=n; l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans+=1LL*(sum[r]-sum[l-1])*1LL*(n/l)*1LL*(m/l);
}
printf("%lld\n",ans);
}
return 0;
}
[HAOI2011] Problem b
(题目传送门)
题意
对于给出的
解题思路
-
类比上面一题
ZAP
,设 表示满足 , ,且 的二元组 的数量 -
类似二维前缀和,容易得出
-
同样,可以一开始先令
除以 ,方便代码实现
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=50010;
int T,a,b,c,d,k;
int prime[N],v[N],tot,mu[N],sum[N];
LL ans;
void primes()
{
mu[1]=1;
for(int i=2; i<=50000; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>50000/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1; i<=50000; i++)
sum[i]=sum[i-1]+mu[i];
}
LL f(int n,int m)
{
if(n>m)
swap(n,m);
LL res=0;
for(int l=1,r; l<=n; l=r+1)
{
r=min(n/(n/l),m/(m/l));
res+=1LL*(sum[r]-sum[l-1])*1LL*(n/l)*1LL*(m/l);
}
return res;
}
int main()
{
primes();
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
ans=0;
ans=f(b/k,d/k)-f((a-1)/k,d/k)-f(b/k,(c-1)/k)+f((a-1)/k,(c-1)/k);
printf("%lld\n",ans);
}
return 0;
}
YY的GCD
(题目传送门)
题意
解题思路
-
设
表示质数集合 -
原题要求的是
-
同除以
得: -
由莫比乌斯反演得
-
我们枚举
得: -
设
,把 提到前面去 -
对于后半部分式子,我们可以在线性筛后预处理出来,再做一次整除分块即可求出答案
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=10000010;
int T,n,m;
int prime[N],v[N],tot,mu[N];
LL sum[N],g[N];
void primes()
{
mu[1]=1;
for(int i=2; i<=1e7; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>1e7/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1; i<=tot; i++)
{
for(int j=1; j*prime[i]<=1e7; j++)
g[prime[i]*j]+=mu[j];
}
for(int i=1; i<=1e7; i++)
sum[i]=sum[i-1]+g[i];
}
LL cacl(int n,int m)
{
if(n>m)
swap(n,m);
LL res=0;
for(int l=1,r; l<=n; l=r+1)
{
r=min(n/(n/l),m/(m/l));
res+=1LL*(sum[r]-sum[l-1])*(n/l)*(m/l);
}
return res;
}
int main()
{
primes();
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
printf("%lld\n",cacl(n,m));
}
return 0;
}
于神之怒加强版
(题目传送门)
题意
给定
对
对于全部的测试点,保证
解题思路
-
直接开始推式子
-
除以
得: -
枚举
得: -
类似上一题的优化,设
得: -
观察到,
是狄利克雷卷积的形式,而 和 又是 积性函数,所以 也是积性函数。因此,我们可以考虑线性筛预处理出 的值 -
设
质因数分解为 ,则 -
观察到,当
时, ,所以 -
这个式子是可以预处理出来的
-
所以
-
利用整除分块即可求解
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5000010;
const int MOD=1e9+7;
int T,k,n,m;
int prime[N],v[N],tot;
LL g[N],mi[N],sum[N];
LL ksm(int a,int b)
{
if(b==0)
return 1;
if(b==1)
return a;
LL tmp=ksm(a,b/2);
if(b%2==0)
return (tmp*tmp)%MOD;
else
return ((tmp*tmp)%MOD*(LL)a)%MOD;
}
void primes()
{
g[1]=1;
for(int i=2; i<=5e6; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mi[i]=ksm(i,k);
g[i]=(mi[i]-1+MOD)%MOD; //i为质数时g(i)=i^k-1
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>5e6/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
g[i*prime[j]]=(g[i]*mi[prime[j]])%MOD; //当i和prime[j]不互质时,读者可自行证明该结论正确性
else
g[i*prime[j]]=(g[i]*g[prime[j]])%MOD;
}
}
for(int i=1; i<=5e6; i++)
sum[i]=sum[i-1]+g[i];
}
LL cacl(int n,int m)
{
if(n>m)
swap(n,m);
LL res=0;
for(int l=1,r; l<=n; l=r+1)
{
r=min(n/(n/l),m/(m/l));
res=(res+((sum[r]-sum[l-1]+MOD)%MOD)*(n/l)%MOD*(m/l)%MOD)%MOD;
}
return res;
}
int main()
{
scanf("%d%d",&T,&k);
primes();
while(T--)
{
scanf("%d%d",&n,&m);
printf("%lld\n",cacl(n,m));
}
return 0;
}
[国家集训队] Crash的数字表格 / JZPTAB
(题目传送门)
题意
给定
解题思路
-
对
变换一下得: -
枚举
得: -
除以
得: -
把后半部分式子单独提出来,设:
-
化简得:
-
设
-
化简得:
-
所以,求出
的时间复杂度为 -
将
代回 中得: -
我们利用整除分块即可求出
-
将
代回 得: -
再做一次整除分块即可求解
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=10000010;
const int MOD=20101009;
int n,m;
int prime[N],v[N],tot,mu[N];
LL sum[N];
void primes()
{
mu[1]=1;
for(int i=2; i<=1e7; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>1e7/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1; i<=1e7; i++)
sum[i]=(sum[i-1]+1LL*(mu[i]+MOD)%MOD*i%MOD*i%MOD)%MOD;
}
LL g(int a,int b)
{
return (1LL*a*(a+1)/2%MOD)*(1LL*b*(b+1)/2%MOD)%MOD;
}
LL f(int a,int b)
{
if(a>b)
swap(a,b);
LL res=0;
for(int l=1,r; l<=a; l=r+1)
{
r=min(a/(a/l),b/(b/l));
res=(res+(sum[r]-sum[l-1]+MOD)%MOD*g(a/l,b/l)%MOD)%MOD;
}
return res;
}
LL calc(int a,int b)
{
if(a>b)
swap(a,b);
LL res=0;
for(int l=1,r; l<=a; l=r+1)
{
r=min(a/(a/l),b/(b/l));
res=(res+1LL*(r-l+1)*(l+r)/2%MOD*f(a/l,b/l)%MOD)%MOD;
}
return res;
}
int main()
{
primes();
scanf("%d%d",&n,&m);
printf("%lld",calc(n,m));
return 0;
}
[SDOI2014] 数表
(题目传送门)
题意
有一张
解题思路
-
设
表示 的因数之和 -
则
-
枚举
得: -
由莫比乌斯反演得:
-
套路设
得: -
可以在 的时间预处理出来 -
因为有
的限制,当 变大时,后半部分式子也在改变。所以考虑将询问离线,按 的值从小到大排序,每次 变大时都类似插入操作,考虑用树状数组维护 -
设
,对于所有 来说,它会对 产生贡献,所以给每个 都加上 即可
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=100010,Q=20010;
const LL MOD=2147483648;
struct node
{
int dat;
int id;
}f[N];
struct que
{
int n,m,s,id;
}a[Q];
int T;
int prime[N],v[N],tot,mu[N];
LL c[N],ans[N];
bool cmp(que xx,que yy)
{
return xx.s<yy.s;
}
bool cmp2(node xx,node yy)
{
return xx.dat<yy.dat;
}
void add(int x,LL y)
{
for(; x<=1e5; x+=(x&-x))
c[x]=(c[x]+y)%MOD;
}
LL ask(int x)
{
LL s=0;
for(; x; x-=(x&-x))
s=(c[x]+s)%MOD;
return s;
}
void update(int x,LL y)
{
for(int i=1; i*x<=1e5; i++)
add(i*x,1LL*mu[i]*y);
}
void primes()
{
mu[1]=1;
for(int i=2; i<=1e5; i++)
{
if(!v[i])
{
v[i]=i;
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1; j<=tot; j++)
{
if(prime[j]>v[i] || prime[j]>1e5/i)
break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1; i<=1e5; i++)
for(int j=1; i*j<=1e5; j++)
f[i*j].dat=(f[i*j].dat+i)%MOD;
for(int i=1; i<=1e5; i++)
f[i].id=i;
sort(f+1,f+1+100000,cmp2);
}
LL calc(int a,int b)
{
if(a>b)
swap(a,b);
LL res=0;
for(int l=1,r; l<=a; l=r+1)
{
r=min(a/(a/l),b/(b/l));
res=(res+(ask(r)-ask(l-1)+MOD)%MOD*1LL*(a/l)%MOD*(b/l)%MOD)%MOD;
}
return res;
}
int main()
{
primes();
scanf("%d",&T);
for(int i=1; i<=T; i++)
scanf("%d%d%d",&a[i].n,&a[i].m,&a[i].s),a[i].id=i;
sort(a+1,a+1+T,cmp);
int p=1;
for(int i=1; i<=T; i++)
{
while(f[p].dat<=a[i].s && p<=1e5)
update(f[p].id,f[p].dat),p++;
ans[a[i].id]=calc(a[i].n,a[i].m);
}
for(int i=1; i<=T; i++)
printf("%lld\n",ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?