[BZOJ1101][POI2007]Zap 莫比乌斯反演

1101: [POI2007]Zap

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2796  Solved: 1201
[Submit][Status][Discuss]

Description

  FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a
,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。

Input

  第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个
正整数,分别为a,b,d。(1<=d<=a,b<=50000)

Output

  对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。

Sample Input

2
4 5 2
6 4 3

Sample Output

3
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。

HINT

 

Source

 

 

要求gcd(x,y)=d只要求 ∑(1≤x≤a/d)∑(1≤y≤b/d)[gcd(x,y)=1]

由于莫比乌斯函数性质∑(k|n)µ(k)只有当n等于1时函数值为1,其余函数值为0。

所以可以转换为求∑(1≤x≤a/d)∑(1≤y≤b/d)∑(k|gcd(x,y))µ(k)

把k提到第一位枚举后可化为∑(1≤k≤min(a/d,b/d))∑(k|x)∑(k|y) µ(k)

合并后两位可化为 ∑(1≤k≤min(a/d,b/d)) µ(k)*(a/d/k)*(b/d/k)

按(a/d/k)*(b/d/k)分组即可o(√n) 查询

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<cstdio>
 6 #include<algorithm>
 7 #define maxn 50005
 8 #define LL long long
 9 using namespace std;
10 bool vis[maxn];
11 int prime[maxn],cnt,u[maxn],sum[maxn];
12 void init() {
13     u[1]=sum[1]=1;
14     for(int i=2;i<maxn;i++) {
15         if(!vis[i]) prime[++cnt]=i,u[i]=-1; 
16         for(int j=1;i*prime[j]<maxn&&j<=cnt;j++) {
17             vis[i*prime[j]]=1;
18             if(i%prime[j]==0) {u[i*prime[j]]=0;break;}
19             u[i*prime[j]]=-u[i];
20         }
21         sum[i]=sum[i-1]+u[i];
22     }
23     return ;
24 }
25 int a,b,c,d,k,t;
26 LL f(int n,int m,int k) {
27     LL ans=0;
28     n/=k;m/=k;
29     for(int i=1,lst;i<=min(n,m);i=lst+1) {
30         lst=min(n/(n/i),m/(m/i));
31         ans+=(LL)(n/i)*(m/i)*(sum[lst]-sum[i-1]);
32     }
33     return ans;
34 }
35 int main() {
36     init();
37     scanf("%d",&t);
38     while(t--) {
39         scanf("%d%d%d",&a,&b,&k);
40         printf("%lld\n",f(a,b,k));
41     }
42 }
View Code

 

posted @ 2017-12-12 18:29  wls001  阅读(114)  评论(0编辑  收藏  举报