很久以前就学了莫比乌斯反演,然而一直都木有来写一个总结,省选完后今日来补坑…
其实莫比乌斯反演就是一个公式…
证明如下:
之后的问题就变成了如何求莫比乌斯函数?
若 d=1 ,则 μ(d)=1 ;
若 d=p1∗p2∗...∗pk ,则 μ(d)=(−1)k ;
否则 μ(d)=0 。
u[1]=1;
for(int i=2, k;iif(!flag[i])prime[++tot]=i, u[i]=-1;
for(int j=1;j<=tot&&(k=i*prime[j])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
#include
#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;iif(!flag[i])
{
prime[++cnt]=i;
u[i]=-1;
}
for(int j=1;prime[j]*i*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的漂亮啊…
我们来看一看柿子:
int u[MAXN], prime[MAXN], tot;
bool flag[MAXN];
LL f[MAXN];
void init()
{
u[1]=1;
for(int i=2, k;iif(!flag[i])prime[++tot]=i, u[i]=-1;
for(int j=1;j<=tot&&(k=i*prime[j])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*k]+=u[j];
}
for(int i=1;i1];
}
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;
}