GCD
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 10197 Accepted Submission(s): 3848
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.
Yoiu can assume that a = c = 1 in all test cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.
2 1 3 1 5 1 1 11014 1 14409 9
Case 1: 9 Case 2: 736427HintFor the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).
题意:
给定(a,b)和(c,d)两区间,分别选择一个数字使得gcd(x,y)==k
问:这样的数对有多少对?
题解:
莫比乌斯反演,下面的链接讲解的蛮清晰的
http://wenku.baidu.com/link?url=2rQAVIT4PxnC99LQxtLTK9-1dN3QpWGoZ6ECXFTFGzINi_1PVS3YYC0Ai22rlFPhiz0dXgafMlAcgS2AhQfBt4yNYP5nqqbssxtxj8eqZbi
【a,b】和【c,d/】同时除以k得到【1,b/k】和【1,d/k 】
问题转化为从【1,b/k】和【1,d/k 】分别选择一个数字使得gcd(x,y)==1
莫比乌斯反演的两种形式:
上面的一种形式相对来说比较好求,μ(d)函数就是下面代码中的mu数组
莫比乌斯函数μ(d)定义
编辑
若d=1 那么μ(d)=1
若d=p1p2…pr (r个不同质数,且次数都为一)μ(d)=(-1)r
其余 μ(d)=0
莫比乌斯函数的性质:
F(d)表示 有多少对(x,y)满足 gcd(x,y)== d *k(k为任意正整数)
f(d) 表示有多少对(x,y)满足 gcd(x,y)== d
易求得:
对于上叙公式的理解:
假设当 gcd (a,b)== x=2,n=8,m=11
在两个区间【1,8】和【1,11 】中选择2的倍数
第一个区间选择2 第二个区间可以选择 2 4 6 8 10
第一个区间选择3 第二个区间可以选择 2 4 6 8 10
第一个区间选择4 第二个区间可以选择 2 4 6 8 10
第一个区间选择6 第二个区间可以选择 2 4 6 8 10
任意两个数字组合得到的(a,b)计算最大公约数都是x的倍数
这样的数对有(8 / 2)*(11 / 2)==20个
注意:
(1,2)和(2,1)算一种情况,那么我们就要减去多余了的情况,由下图知
(图片借另外一篇bloghttp://blog.csdn.net/lixuepeng_001/article/details/50577932)
G(b,b)就是多算进去的这些情况
那么 G(b,d)- G(b,b)/ 2 就是最终结果
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAXN 100000
bool check[MAXN+10];
int primer[MAXN+10];
int mu[MAXN+10];
void Moblus()
{
mu[1]=1;
int tot=1;
for(int i=2;i<=MAXN;i++){
if(!check[i]){
primer[tot++]=i;
mu[i]=-1;
}
for(int j=1;j<tot&&i*primer[j]<=MAXN;j++){
check[i*primer[j]]=true;
if(i%primer[j]==0){
mu[i*primer[j]]=0;
break;
}
mu[i*primer[j]]=-mu[i];
}
}
}
int main()
{
int T;
Moblus();
int cases=1,a,b,c,d,k;
//freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(!k){
printf("Case %d: 0\n",cases++);
continue;
}
b/=k;
d/=k;
if(b>d)
swap(b,d);
long long ans1=0,ans2=0;
for(int i=1;i<=b;i++)
ans1+=(long long)mu[i]*(b/i)*(d/i);
for(int i=1;i<=b;i++)
ans2+=(long long)mu[i]*(b/i)*(b/i);
printf("Case %d: %I64d\n",cases++,ans1-ans2/2);
}
return 0;
}
维护一个前缀和进行优化
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAXN 100000
bool check[MAXN+10];
int primer[MAXN+10];
int mu[MAXN+10];
int sum[MAXN+10];
void Moblus()
{
memset(check,0,sizeof(check));
mu[1]=1;
int tot=0;
for(int i=2;i<=MAXN;i++){
if(!check[i]){
primer[tot++]=i;
mu[i]=-1;
}
for(int j=0;j<tot&&i*primer[j]<=MAXN;j++){
check[i*primer[j]]=true;
if(i%primer[j]==0){
mu[i*primer[j]]=0;
break;
}
mu[i*primer[j]]=-mu[i];
}
}
sum[0]=0;
for(int i=1;i<=MAXN;i++)
sum[i]=sum[i-1]+mu[i];
}
int main()
{
int T;
Moblus();
int cases=1,a,b,c,d,k;
//freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(!k){
printf("Case %d: 0\n",cases++);
break;
}
b/=k;
d/=k;
if(b>d)
swap(b,d);
int last;
long long ans1=0,ans2=0;
for(int i=1;i<=b;i=last+1){
last=min(b/(b/i),d/(d/i));
ans1+=(long long)(sum[last]-sum[i-1])*(b/i)*(d/i);
}
for(int i=1;i<=b;i=last+1){
last=b/(b/i);
ans2+=(long long)(sum[last]-sum[i-1])*(b/i)*(b/i);
}
printf("Case %d: %I64d\n",cases++,ans1-ans2/2);
}
return 0;
}