BZOJ1101: [POI2007]Zap(莫比乌斯反演)

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

解题思路:

${\sum_{i=1}^{n}}{\sum_{j=1}^{m}}gcd(i,j)==d$

$={\sum_{d|i}^{n}}{\sum_{d|j}^{m}}\varepsilon(gcd(\frac{i}{d},\frac{j}{d}))$

设${M={\left \lfloor {\frac{m}{d}} \right \rfloor}},{N={\left \lfloor {\frac{n}{d}} \right \rfloor}}$

$原式={\sum_{i=1}^{N}}{\sum_{j=1}^{M}}\varepsilon(gcd(i,j))$

$={\sum_{i=1}^{N}}{\sum_{j=1}^{M}}{\sum_{d|gcd(i,j)}}{\mu (d)}$

$={\sum_{d=1}^{min(N,M)}}{\sum_{d|i}^{N}}{\sum_{d|j}^{M}}{\mu(d)}$

$={\sum_{d=1}^{min(N,M)}}{\mu(d)}{\sum_{d|i}^{N}}1{\sum_{d|j}^{M}}1$

$={\sum_{d=1}^{min(N,M)}}{\mu(d)}{\sum_{i=1}^{\left \lfloor {\frac{N}{d}} \right \rfloor}}1{\sum_{j=1}^{\left\lfloor{\frac{M}{d}}\right\rfloor}}1$

$={\sum_{d=1}^{min(N,M)}}{\mu(d)}{\left\lfloor{\frac{N}{d}}\right\rfloor}{\left\lfloor{\frac{M}{d}}\right\rfloor}$

最后整除分块预处理$\mu$的前缀和搞一下就好了

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 const int N=50010;
 5 int prime[N];
 6 int miu[N];
 7 int s[N];
 8 bool vis[N];
 9 int cnt;
10 int T;
11 int a,b,d;
12 void gtp(void)
13 {
14     miu[1]=1;
15     for(int i=2;i<N;i++)
16     {
17         if(!vis[i])
18         {
19             prime[++cnt]=i;
20             miu[i]=-1;
21         }
22         for(int j=1;j<=cnt&&prime[j]*i<N;j++)
23         {
24             vis[prime[j]*i]=true;
25             if(i%prime[j]==0)
26             {
27                 miu[i*prime[j]]=0;
28                 break;
29             }
30             miu[prime[j]*i]=-miu[i];
31         }
32     }
33     for(int i=1;i<N;i++)
34         s[i]=s[i-1]+miu[i];
35     return ;
36 }
37 int main()
38 {
39     gtp();
40     scanf("%d",&T);
41     while(T--)
42     {
43         scanf("%d%d%d",&a,&b,&d);
44         a=a/d;
45         b=b/d;
46         int c=std::min(a,b);
47         int ans=0;
48         for(int k=1,u;k<=c;k=u+1)
49         {
50             u=std::min(a/(a/k),b/(b/k));
51             ans+=(s[u]-s[k-1])*(a/k)*(b/k);
52         }
53         printf("%d\n",ans);
54     }
55     return 0;
56 }
posted @ 2018-12-22 15:42  Unstoppable728  阅读(162)  评论(0编辑  收藏  举报