莫比乌斯反演+整除分块

莫比乌斯反演:
若:
F(n)=∑d|n {f(d)}
则:
f(n)=∑d|n {μ(d)*F(⌊n/d⌋)}
或者是:
若:
F(n)=∑n|d {f(d)}
则:
f(n)=∑n|d {μ(d/n)F(d)}
两种情况。
题目:洛谷P3455
莫比乌斯反演模版题:求满足 1≤x≤a,1≤y≤b且 gcd(x,y)=k 的二元组 (x,y) 的数量。

  1. gcd(x,y)==k 推出 gcd(x/k,y/k)==1;
  2. 应此我们只需求出在1 到 min(a,b) /k范围内gcd(x,y)==1的二元组即可,即求f(1);
  3. 设f(d)为gcd(x,y)==d 的二元组数量,F[n]为 F(n)=∑n|d {f(d)},F[n]表示的是 1 到 min(a,b) 所有gcd(x,y) 是n的倍数的二元组数量。
  4. 因此推出F[n] = (a/n)*(b/n);
  5. 由反演可得: f(n)=∑n|d {μ(d/n)F(d)} 变换可得 f(1)=∑i=1 {μ[i]*F[i]};
    这道题如果跑裸的公式会超时,需要借助整除分块。
    整除分块:
    很方便的工具 计算sum (1到 n)n/i (整除) ;
int ans=0;
for(int l=1,r;i<=n;l=r+1)
{
	r=n/(n/l);
	ans+=(r-l+1)*(n/l);
}

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;
const int N=54321;
ll prime[N],mu[N],sum[N];
bool p[N];
void get_mu()    //线性筛,筛选出莫比乌斯函数;
{
    int cnt=0;
    mu[1]=sum[1]=1;
    for(int i=2;i<N;++i){
        if(!p[i]){
            prime[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt&&i*prime[j]<N;++j){
            p[i*prime[j]]=true;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
        sum[i]=mu[i]+sum[i-1];
    }
}
int main()
{
    ll t,a,b,k,m;
    get_mu();
    scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld%lld",&a,&b,&k);
        ll ans=0;
        a/=k,b/=k;
        if(a>b){
            m=a;
            a=b;
            b=m;
        }
        for(int l=1,r;l<=a;l=r+1){   // 此处需要理解整除分块;
            r=min(a/(a/l),b/(b/l));
            ans+=(sum[r]-sum[l-1])*(a/l)*(b/l);
        }
        printf("%lld\n",ans);
    }
    return 0;
}~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值