莫比烏斯反演

大佬博客
hdu 1695
題意:讓求從a~b區間和c~d區間有多少對的數的gcd爲k,不能有重複的,gcd(2,3)和gcd(3,2)算重複的。
莫比烏斯函數應用的經典題,先求出所有的值然後再減去重複區間的值就好了。

假設F(n)的值爲gcd(x,y)==n(a<=x<=b,c<=y<=d)的有多少種。
G(n)代表n|gcd(x,y),(a<=x<=b,c<=y<=d)的有多少種。
由莫比烏斯反演得F(n)=sigma(u(d/n)*G(d)),(n|d),sigma爲求和的意思。
由題意可知G(n)==(b/n-(a-1)/n)*(d/n-(c-1)/n)。
求F(k)即可,記得要減去重複的。
這裏有有關莫比烏斯函數篩法的求解過程

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const LL mod=1e9+7;
int p[maxn],mu[maxn];
bool vis[maxn];
void mobius()//求莫比烏斯函數
{
    int cont=0;
    mu[1]=1;
    for(int i=2; i<maxn; i++)
    {
        if(!vis[i])
        {
            mu[i]=-1;
            p[cont++]=i;
        }
        for(int j=0; j<cont&&p[j]*i<maxn; j++)
        {
            vis[p[j]*i]=true;
            if(i%p[j]==0)
            {
                mu[p[j]*i]=0;
                break;
            }
            else
                mu[p[j]*i]=-mu[i];
        }
    }
}
int main()
{
    mobius();
    int ncase,Z=0;
    scanf("%d",&ncase);
    while(ncase--)
    {
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",++Z);
            continue;
        }
        LL ans=0;
        int n=(b,d);
        for(int i=k; i<=n; i+=k)//算出所有的情況
            ans+=1ll*mu[i/k]*(b/i-(a-1)/i)*(d/i-(c-1)/i);
        LL ans2=0;
        int l=max(a,c),r=min(b,d);//求出相交區間
        if(l<=r)
            for(int i=k; i<=r; i+=k)//算出相交區間
                ans2+=1ll*mu[i/k]*(r/i-(l-1)/i)*(r/i-(l-1)/i);
        ans-=ans2/2;//因爲相交區間的也算的是重複點的所以要除2
        printf("Case %d: %lld\n",++Z,ans);
    }
}
發佈了110 篇原創文章 · 獲贊 18 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章