前言
很久以前就学了莫比乌斯反演,然而一直都木有来写一个总结,省选完后今日来补坑…
姿势
其实莫比乌斯反演就是一个公式…
证明如下:
之后的问题就变成了如何求莫比乌斯函数?
若
d=1
,则
μ(d)=1
;
若
d=p1∗p2∗...∗pk
,则
μ(d)=(−1)k
;
否则
μ(d)=0
。
u[1]=1;
for(int i=2, k;i<MAXN;++i)
{
if(!flag[i])prime[++tot]=i, u[i]=-1;
for(int j=1;j<=tot&&(k=i*prime[j])<MAXN;++j)
{
flag[k]=1;
if(i%prime[j]==0){u[k]=0;break;}
u[k]=-u[i];
}
}
下面是莫比乌斯反演的一个有趣的性质…
证明:
例题
还是直接上题吧…
hdu1695
转送门
题目大意:求满足
gcd(x,y)=k(1≤x≤b
1≤y≤d)
的无序数对
(x,y)
的个数。
莫比乌斯反演最基本的应用,当然也可以选择容斥原理。突然觉得莫比乌斯反演就是容斥原理…
令
F(d)
表示
d|gcd(x,y)
的对数,那么
f(d)
就表示
gcd(x,y)=d
的对数。
很显然
F(d)=(x/d)∗(y/d)
所以就直接上莫比乌斯反演吧….
当初写的时候一直RE,然后发现k竟然可以为0!!!
#include <iostream>
#include <cstdio>
#define MAXN 100005
#define LL long long int
using namespace std;
int a, b, c, d, k;
bool flag[MAXN];
int u[MAXN], prime[MAXN], cnt;
void mobius()
{
u[1]=1;
for(int i=2;i<MAXN;++i)
{
if(!flag[i])
{
prime[++cnt]=i;
u[i]=-1;
}
for(int j=1;prime[j]*i<MAXN&&j<=cnt;++j)
{
flag[prime[j]*i]=1;
if(i%prime[j]==0){u[i*prime[j]]=0;break;}
u[i*prime[j]]=-u[i];
}
}
}
LL solve(LL n,LL m)
{
n/=k, m/=k;
LL ans=0;
for(int i=1;i<=n;++i)
{
ans+=(n/i)*(m/i)*u[i];
}
return ans;
}
int main()
{
mobius();
int cas, CNT=0;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==0)
{
printf("Case %d: 0\n",++CNT);
continue;
}
if(b>d)swap(b,d);
printf("Case %d: %I64d\n",++CNT,solve(b,d)-solve(b,b)/2);
}
return 0;
}
SPOJVLATTICE
转送门
大意说不太清楚…
然后感觉就是求
gcd(a,b,c)=1
的对数…
就直接上吧…
bzoj2301
转送门
和上面那题特别相像,然后我就交了一发…T辣…
然后有一个最基本的分块优化…
F(d)=⌊nd∗md⌋
其实不同的F只有
2∗n−−√
个…
所以有一些可以一起算的…
LL solve(int a,int b)
{
if(a>b)swap(a,b);
LL ans=0;
for(int i=1, last;i<=a;i=last+1)
{
last=min(a/(a/i),b/(b/i));
ans+=(f[last]-f[i-1])*(a/i)*(b/i);
}
return ans;
}
bzoj2820
权限题无转送门,有一题一模一样的题在spoj上 转送门
题目大意:求有多少数对
(x,y)(1≤x≤n,1≤y≤m)
满足
gcd(x,y)
为质数。
一个很native的想法是枚举质数,然后就是上面那道题辣…
然后T的漂亮啊…
我们来看一看柿子:
令 t=p∗d ,然后柿子就变成了:
然后 ∑p|tμ(tp) 就可以用筛法乱搞搞辣…
int u[MAXN], prime[MAXN], tot;
bool flag[MAXN];
LL f[MAXN];
void init()
{
u[1]=1;
for(int i=2, k;i<MAXN;++i)
{
if(!flag[i])prime[++tot]=i, u[i]=-1;
for(int j=1;j<=tot&&(k=i*prime[j])<MAXN;++j)
{
flag[k]=1;
if(i%prime[j]==0){u[k]=0;break;}
u[k]=-u[i];
}
}
for(int i=1, k;i<=tot;++i)
{
k=prime[i];
for(int j=1;j*k<MAXN;++j)f[j*k]+=u[j];
}
for(int i=1;i<MAXN;++i)f[i]+=f[i-1];
}
LL solve(int a,int b)
{
if(a>b)swap(a,b);
LL ans=0;
for(int i=1, last;i<=a;i=last+1)
{
last=min(a/(a/i),b/(b/i));
ans+=(f[last]-f[i-1])*(a/i)*(b/i);
}
return ans;
}
int main()
{
init();
int cas, u, v;
GET(cas);
while(cas--)
{
GET(u), GET(v);
printf("%lld\n",solve(u,v));
}
return 0;
}