【HYSBZ 2301】——莫比乌斯反演

Time Limit: 50 Sec Memory Limit: 256 MB

Description

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

Input

第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

Output

共n行,每行一个整数表示满足要求的数对(x,y)的个数

Sample Input
2

2 5 1 5 1

1 5 1 5 2

Sample Output

14

3

HINT

100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000

第一次写莫比乌斯反演的题,WA了好几次,竟然是爆int了,QAQ.

那么什么是莫比乌斯反演呢?

定理: f(n) g(n) 是定义在非负整数集合上的两个函数,并且满足 g(n)=d|nf(d) 条件,那么我们得到结论 f(n)=d|nμ(d)g(nd)=n|dμ(dn)g(d) ,在这个公式中有一个 μ(d) 函数,它的定义如下
1. 若 d = 1,那么 μ(d)=1
2. 若 d=p1p2pk ,那么 μ(d)=(1)k
3. 其他情况为0.
它有如下的性质:
1. 对于任意正整数n有 d|nμ(d)=[n=1]
2. d|nμ(d)d=φ(n)n
在这个题中,我们定义f(n)表示gcd(x,y) =n的数量,g(n)表示n|gcd(x,y)的数量,
所以 f(n)=n|dμ(dn)g(d) 由于n=1,所以 f(1)=dμ(d)g(d)=dμ(d)[nd][md]

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int Max = 50100;

int mu[Max];

int prime[Max],cnt;

bool vis[Max];

int pre[Max];

void GetMu()
{
    memset(vis,false,sizeof(vis));

    mu[1] = 1; cnt = 0;pre[1] = 1;

    for(int i = 2 ; i < Max; i++)   
    {
        if(!vis[i])
        {
            prime[cnt++] = i;

            mu[i] = -1;
        }
        for(int j = 0;j < cnt && i * prime[j] < Max; j++)
        {
            vis[i*prime[j]] = true;

            if(i%prime[j]) mu[i*prime[j]] = -mu[i];
            else
            {
                mu[i*prime[j]] = 0;

                break;
            }
        }

        pre[i] = pre[i-1]+mu[i];
    }


}

LL Cal(LL n,LL m)
{
    int  t = min(n,m);

    LL sum  = 0;

    int end;

    for(int i = 1;i<= t; i =end+1)
    {
        end = min(n/(n/i),m/(m/i));

        sum+=((pre[end]-pre[i-1])*(n/i)*(m/i));
    }

    return sum;
}


int main() 
{
    int T;

    int a,b,c,d,k;

    GetMu();

    scanf("%d",&T);

    while(T--)
    {
        scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);

        LL sum = Cal(b/k,d/k)-Cal((a-1)/k,d/k)-Cal((c-1)/k,b/k)+Cal((a-1)/k,(c-1)/k);

        printf("%lld\n",sum);
    }
    return 0;
}

你可能感兴趣的:(计算方法)