莫比乌斯反演

题目连接

题目大意:从(1,b)中选一个数x,(1,d)中选一个数y。求gcd(x,y)=k的个数(3,4),(4,3)只算一遍。

莫比乌斯反演百科

gcd(x,y) == k等价于gcd(x/k,y/k)==1

那么题目就变为在[1,b/k],[,d/k]中选择x,y满足gcd(x,y) == 1 的个数了 ,然后去重即可.

我们可以假设

  F(k):表示gcd(x,y) == (k的倍数的个数).

  f(k):表示gcd(x,y)==k的个数。

            (k|d表示d能够整除k)  

 

      

由F函数的定义有

而我们最后要求得则是f(1) .

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<iomanip>
#include<vector> 
#include<functional>
using namespace std;
typedef long long ll;

const int maxn = 100005;

bool check[maxn];
int prime[maxn];
int mu[maxn];

void Moblus(){                //莫比乌斯的各项系数打表 
    memset(check,false,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i = 2;i<maxn;i++)
    {
        if(!check[i])
        {
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j =0;j<tot;j++)
        {
            if(i*prime[j]>maxn)
                break;
            check[i*prime[j]] = true;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            else
                mu[i*prime[j]] = -mu[i];
        }
    }
}  

int main(){
    int T;
    scanf("%d",&T);
    Moblus();
    for(int kase = 1;kase<=T;kase++)
    {
        int a,b,c,d,k;
        cin>>a>>b>>c>>d>>k;
        if(k==0)
        {
            cout<<"Case "<<kase<<": 0"<<endl;
            continue;
        }

        b /=k;
        d /=k;
        if(b>d)
            swap(b,d);
        ll ans1 = 0;
        for(int i =1;i<=b;i++)
            ans1 += (ll)mu[i] * (b/i) * (d/i);        //F(1) = f(1)+f(2)+....+f(d)
        ll ans2 = 0;
        for(int i =1;i<=b;i++)                    //重复部分(3,4)与(4,3)  x,y都属于1~b  的那部分 
            ans2 += (ll)mu[i] * (b/i) * (b/i);
        printf("Case %d: %lld\n",kase,ans1-ans2/2);
    }
    return 0;
}

 

posted @ 2018-08-31 12:05  chase丶月光  阅读(111)  评论(0)    收藏  举报