HDU-1695(水题3)

莫比乌斯反演入门题

大佬们口中的最后一道水题(我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;
}
发布了12 篇原创文章 · 获赞 0 · 访问量 274

猜你喜欢

转载自blog.csdn.net/zhlnb/article/details/104356571