莫比烏斯反演+整除分塊

莫比烏斯反演:
若:
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;
}~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章