开坑,填坑——莫比乌斯反演

hdu 1695

题目:给出x和y的范围,要求gcd(x,y)==k的数对个数。

思路:首先把范围除k,然后就是求gcd(x,y)=1的数对个数。具体莫比乌斯公式的用法还不是很懂,目前的理解是这样的:

莫比乌斯公式给出了一个从和函数反演到原函数的方法。对于一个定义在正整数上的函数,其和函数F(n)定义为所有f(d)|d是n的因子的和。然后根据莫比乌斯公式,可由F求得f。

但是此题的形式有点不同。设f(k)表示gcd(x,y)=k的数对的个数。然后设F(k)表示gcd(x,y)=k的倍数的数对的个数。然后F是f的和函数,但是形式变成了这样:F(n)定义为所有f(d)|d是n的倍数的和。此时应用莫比乌斯函数的形式不变。

/*
* @author:  Cwind
*/
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-3)
#define IINF (1<<29)
#define LINF (1ll<<59)
#define INF 1000000000
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> P;

zconst int MAXN = 1000000;
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
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 a,b,c,d,k;
int T;
int cas=0;
int main(){
    freopen("/home/files/CppFiles/in","r",stdin);
    //freopen("defense.in","r",stdin);
    //freopen("defense.out","w",stdout);
    cin>>T;
    Moblus();
    while(T--){
        cin>>a>>b>>c>>d>>k;
        if(k==0){
            printf("Case %d: 0\n",++cas);
            continue;
        }
        ll ans1=0,ans2=0;
        if(b>d) swap(b,d);
        b/=k,d/=k;
        for(int i=1;i<=b;i++){
            ans1+=mu[i]*(ll)(b/i)*(d/i);
        }
        for(int i=1;i<=b;i++){
            ans2+=mu[i]*(ll)(b/i)*(b/i);
        }
        printf("Case %d: %lld\n",++cas,ans1-ans2/2);
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Cw-trip/p/4820191.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值