您的位置:首页 > 其它

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

2016-05-11 21:21 267 查看
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)f(n)和g(n)g(n)是定义在非负整数集合上的两个函数,并且满足g(n)=∑d|nf(d)g(n) =\sum\limits_{d|n}f(d) 条件,那么我们得到结论f(n)=∑d|nμ(d)g(nd)=∑n|dμ(dn)g(d)f(n) = \sum\limits_{d|n}\mu(d)g(\frac{n}{d}) = \sum\limits_{n|d}\mu(\frac{d}{n})g(d),在这个公式中有一个μ(d)\mu(d)函数,它的定义如下

1. 若 d = 1,那么 μ(d)=1\mu(d) = 1

2. 若d=p1p2⋯pk均为互异的素数d = p_1p_2\cdots p_k均为互异的素数,那么μ(d)=(−1)k\mu(d) = (-1)^{k}

3. 其他情况为0.

它有如下的性质:

1. 对于任意正整数n有 ∑d|nμ(d)=[n=1]\sum\limits_{d|n}\mu(d) = [n=1]

2. ∑d|nμ(d)d=φ(n)n\sum\limits_{d|n}\frac{\mu(d)}{d} = \frac{\varphi(n)}{n}

在这个题中,我们定义f(n)表示gcd(x,y) =n的数量,g(n)表示n|gcd(x,y)的数量,

所以f(n)=∑n|dμ(dn)g(d)f(n) = \sum\limits_{n|d}\mu(\frac{d}{n})g(d)由于n=1,所以f(1)=∑dμ(d)g(d)=∑dμ(d)[nd][md]f(1) = \sum\limits_{d}\mu(d)g(d) = \sum\limits_{d} \mu(d)[\frac{n}{d}][\frac{m}{d}]。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: