预备知识:
1. 莫比乌斯函数
1.1 莫比乌斯函数
1.2 莫比乌斯函数的性质
证明略。。
#### 实现方法
线性筛。
线性筛法求素数的过程,每求出一个素数,
;
筛非素数的过程,要break的地方,;(因为它有幂数大于1的质因子);否则
;
void getmu(){ cnt=0;mu[1]=1; for(int i=2;i<N;i++){ if(!p[i]){ prime[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&prime[j]*i<N;j++){ p[prime[j]*i]=1; if(i%prime[j]==0){ mu[prime[j]*i]=0; break; } mu[prime[j]*i]=-mu[i]; } } }
2. 莫比乌斯反演
2.1 莫比乌斯反演定理
或者另一种形式为
第二个比较常用。
2.1 莫比乌斯反演定理的证明
https://www.zhihu.com/question/23764267/answer/26007647
popoqqq也给出了一个证明。大体思想和上类似。不过还是觉得狄利克雷卷积看着更清爽。
2.2 莫比乌斯反演定理的应用
约数和与倍数和。
3. 习题
(题解基本都抄自POPOQQQ,感谢。)
bzoj2440: [中山市选2011]完全平方数
题目描述
求第k个不含有平方因子的数。多组数据。
题目分析
首先二分答案 问题转化为求之间有多少个无平方因子数。
根据容斥原理可知 对于sqrt(x)以内所有的质数 有
x以内的无平方因子数
=0个质数乘积的平方的倍数的数的数量(1的倍数)
-每个质数的平方的倍数的数的数量(9的倍数,25的倍数,...)
+每2个质数乘积的平方的倍数的数的数量(36的倍数,100的倍数,...)-...
容易发现每个乘积a前面的符号恰好是(例如
,故9对答案的贡献为负;
,故36对答案的贡献为正)
以内
的倍数有
个 故有
这题和莫比乌斯反演没关系,算是莫比乌斯函数的一个应用吧。。。
#include<cstdio> #include<cmath> #define ll long long #define N 50005 using namespace std; bool p[N]={1,1}; int prime[N],mu[N],cnt; int t,n; void getmu(){ cnt=0;mu[1]=1; for(int i=2;i<N;i++){ if(!p[i]){ prime[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&prime[j]*i<N;j++){ p[prime[j]*i]=1; if(i%prime[j]==0){ mu[prime[j]*i]=0; break; } mu[prime[j]*i]=-mu[i]; } } } bool judge(ll x){ int l=sqrt(x),ret=0; for(int i=1;i<=l;i++) ret+=x/i/i*mu[i]; return ret>=n; } int main(){ getmu(); scanf("%d",&t); while(t--){ scanf("%d",&n); ll l=0,r=n+n+1; while(l<r){ ll mid=(l+r)/2; if(judge(mid)) r=mid; else l=mid+1; } printf("%lld\n",l); } return 0; }
bzoj2301: [HAOI2011]Problem b
题目描述
n次询问,每次询问有多少个数对满足
##### 题目分析
首先利用容斥原理将一个询问拆分成四个,每次询问有多少个数对(x,y)满足1<=x<=n,1<=y<=m且gcd(x,y)=k
这个问题等价于询问有多少个数对(x,y)满足1<=x<=,1<=y<=
且gcd(x,y)=1
由于之前的结论,我们可以令f(i)为1<=x<=n,1<=y<=m且gcd(x,y)=i的数对(x,y)的个数,F(i)为1<=x<=n,1<=y<=m且i|gcd(x,y)的数对(x,y)的个数。(倍数和。)
我们求
那么显然有
反演后得
枚举原题中k的每一个倍数,我们就可以O(n)时间处理每个询问了
但是O(n)还是不能胜任本题的数据范围
考虑进一步优化
观察式子,发现最多有
个取值
那么就至多有
个取值
枚举这个取值,对莫比乌斯函数维护一个前缀和,就可以在
时间内出解
总时间复杂度
枚举除法的取值这种方法在莫比乌斯反演的应用当中非常常用,且代码并不难写
#include<cstdio> #include<algorithm> #define N 50005 #define ll long long using namespace std; bool p[N]={1,1}; int prime[N],mu[N],sum[N],cnt,t,a,b,c,d,k; void getmu(){ mu[1]=1;cnt=0; for(int i=2;i<N;i++){ if(!p[i]){ prime[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&i*prime[j]<N;j++){ p[i*prime[j]]=1; if(i%prime[j]==0){ mu[i*prime[j]]=0; break; } mu[i*prime[j]]=-mu[i]; } } for(int i=1;i<N;i++) sum[i]=sum[i-1]+mu[i]; //for(int i=1;i<100;i++) printf("%d\n",mu[i]); } ll getans(int n,int m){ ll ret=0; if(n>m) swap(n,m); for(int i=1,last;i<=n;i=last+1){ last=min(n/(n/i),m/(m/i)); ret+=(ll)(n/i)*(ll)(m/i)*(ll)(sum[last]-sum[i-1]); } return ret; } int main(){ scanf("%d",&t); getmu(); for(int i=1;i<=t;i++){ scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); printf("%lld\n",getans(b/k,d/k)-getans((a-1)/k,d/k)-getans(b/k,(c-1)/k)+getans((a-1)/k,(c-1)/k)); } return 0; }
bzoj2820 YY的GCD
题目描述
求有多少数对(x,y)(1<=x<=n,1<=y<=m)满足gcd(x,y)为质数
n,m<=数据组数<=10000
##### 题目分析
根据上一题,我们枚举每一个质数 那么答案就是
直接做显然TLE 考虑优化
令,那么有
和上题的式子很像。我们如果求出的前缀和,也就可以
的时间内求解了。
枚举每个素数,更新它的倍数。复杂度。
#include<cstdio> #include<algorithm> #define N 10000005 #define ll long long using namespace std; ll ans; int prime[N],mu[N],cnt,a[N],T,n,m; bool p[N]={1,1}; void getmu(){ mu[1]=1; for(int i=2;i<N;i++){ if(!p[i]){ prime[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&prime[j]*i<N;j++){ p[i*prime[j]]=1; if(i%prime[j]==0){ mu[i*prime[j]]=0; break; } mu[i*prime[j]]=-mu[i]; } } } void init(){ for(int i=1;i<=cnt;i++) for(int j=1;j*prime[i]<N;j++) a[prime[i]*j]+=mu[j]; for(int i=1;i<N;i++) a[i]=a[i-1]+a[i]; } void getans(int n,int m){ ans=0; if(n>m) swap(n,m); for(int i=1,last;i<=n;i=last+1){ last=min(n/(n/i),m/(m/i)); ans+=(ll)(n/i)*(m/i)*(a[last]-a[i-1]); } } int main(){ getmu(); init(); scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); getans(n,m); printf("%lld\n",ans); } return 0; }
更后话:
以及自己不会反演啊怎么破눈_눈。