一、莫比乌斯函数
1. 定义
若
2. 性质
性质一
若
,则 。
设
因为
如果
若选择
所以:
将
性质二
是积性函数。
若
当
当
二、莫比乌斯反演的套路
没有算法,全是套路。
1. P3455 [POI2007] ZAP-Queries
求式子:
为书写简便,假设式子中的
套路一
将
化为 。
全部同时除以
即可。
套路二
运用莫比乌斯函数的性质一转换。
由性质一,可得若
,则 ,所以,将 转换一下:
套路三
将
的和号改为枚举 的形式。
因为
,所以 ,如果 ,那么 ,所以:
套路四
让每个函数都只和他前面的和号有关系。
容易发现
和前两个和号没有半毛钱关系,直接提前,所以:
套路五
把判断整除
拆开。
容易发现
一部分等价于 ,也就是 :
此时再用一下套路四:
套路六
把判断整除拆开。
就是求 中, 的倍数的个数,明显可以直接用 求出,所以:
至此,化简完成,
Code:
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=1e5+10;
int n,a,b,c,d,k,ans,q[N];
namespace Mu{
int prime[N],tot,mu[N];
bool isprime[N];
void getmu(int x){
memset(isprime,true,sizeof isprime);
isprime[1]=false;
mu[1]=1;
for(int i=2;i<=x;i++){
if(isprime[i]){
prime[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot and i*prime[j]<=x;j++){
isprime[i*prime[j]]=false;
if(!(i%prime[j]))break;
mu[i*prime[j]]=-mu[i];
}
}
}
}using namespace Mu;
signed main(){
IOS;
getmu(5e4);
for(int i=1;i<=5e4;i++)
q[i]=q[i-1]+mu[i];
cin>>n;
while(n--){
cin>>a>>b>>k;
a/=k;
b/=k;
if(a>b)swap(a,b);
ans=0;
for(int l=1,r;l<=a;l=r+1){
r=min(a/(a/l),b/(b/l));
ans+=(q[r]-q[l-1])*(a/l)*(b/l);
}
cout<<ans<<"\n";
}
return 0;
}2. P4449 于神之怒加强版
求式子:
套路七
将求值部分改为枚举取值并乘上合法个数。
显然的,上面的式子可以枚举
的值,假设 ,那么就可以化简为:
此时,运用套路四:
发现后半部分跟上一题一模一样,直接运用上一题相同的方法化简:
套路八
运用向上/下取整的性质化简。
性质:
所以:
套路九
分母出现两个枚举的数的积时,改为枚举这个积。
令
,所以:
此时,只要求出后半部分
设
于是就可以线性筛了,原式变为:
使用整除分块解决。
3. P1829 [国家集训队] Crash的数字表格 / JZPTAB
求式子:
运用
运用套路七:
运用套路一,但是出现了一个没见过的
所以:
运用套路二;
运用套路三:
运用套路四:
运用套路五:
套路十
将整除中的除数化为
。
因为
,所以这样做可以直接化简掉 这样的东西,所以我们在和号的边界里除掉一个 ,再在求值里乘回一个 :
再次运用套路四:
后面两个和号明显就是一个等差数列,直接变成式子:
化简完成,但是这式子也太抽象了,根本没法求,想办法通过用多几个函数来表示方便一点:
观察到第二个和号开始后面的部分可以用整除分块求,那么定义一个函数
观察到最后的两个分式可以
使用整除分块解决。