莫比乌斯反演入门题
大佬们口中的最后一道水题(我c了)
题意:给你五个数,a,b,c,d,k,分别从a-b和c-d两个区间中分别取两个数x,y,使得gcd(x,y)等于k,问这样的(x,y)有多少对(这里(x,y)和(y,x)相同)
题目中特别标明了a和c都当1处理。
题解:因为是1——b和1——d选两个数使gcd等于k嘛,很容易转换为1——b/k和1——d/k中有多少对数互质,我们设f(t)等于gcd(x,y)等于t的(x,y)的数量,F(t)等于gcd(x,y)=t的倍数的(x,y)的数量,很容易得出F(n)= ∑ f(d)(n|d),根据莫比乌斯反演第二条公式我们可以得出f(n)=Σ(mob[d/n] * F[d])(n|d),而我们根据前面的结论,我们在这里就求f(1)就行了。
仔细思考一下,我们可以求出F(x)=(b/x)*(d/x),奉上代码:
//#pragma GCC optimize(3,"Ofast","inline")
//#include<unordered_map>
//#include<unordered_set>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<functional>
#include<cstring>
#include<string>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
const ll mod=1e9+7;
ll prime[maxn],isprime[maxn];
ll mob[maxn];
ll num;
void getmobius()
{
memset(prime,0,sizeof(prime));
memset(mob,0,sizeof(mob));
memset(isprime,1,sizeof(isprime));
mob[1]=1;
for(int i=2;i<=100000;i++)
{
if(isprime[i])
{
prime[num++]=i;
mob[i]=-1;
}
for(int j=0;j<num&&i*prime[j]<=100000;j++)
{
isprime[i*prime[j]]=0;
if(i%prime[j]==0)
{
mob[i*prime[j]]=0;
break;
}
else mob[i*prime[j]]=-mob[i];
}
}
}
int main()
{
ll t;
getmobius();
scanf("%lld",&t);
ll a,b,c,d,k;
int K=0;
while(t--)
{
scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
if(k==0)
{
printf("Case %d: 0\n",++K);
continue;
}
b/=k;d/=k;
ll ans1,ans2;
ans1=ans2=0;
for(ll i=1;i<=min(b,d);i++)ans1+=mob[i]*(b/i)*(d/i);//这里求的是所有的情况,但是(x,y)和(y,x)是一样的情况,所以这不是最终答案
for(ll i=1;i<=min(b,d);i++)ans2+=mob[i]*(min(b,d)/i)*(min(b,d)/i);//这里求的是1-b和1-d重叠部分的所有情况
ans1=ans1-ans2/2;//我们这样想,ans1代表的是所有情况,重叠部分可以说是都算了两次,非重叠部分很正常,只算了一次,然后我们讲ans1减去一个ans/2,就是减去一半的重叠部分,最后就是一个重叠部分和一个非重叠部分的最终答案了!(有点像容斥原理)
printf("Case %d: %lld\n",++K,ans1);
}
return 0;
}