BZOJ1101 [POI2007] Zap

1101: [POI2007]Zap

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2690  Solved: 1137
[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

 

【题解】

莫比乌斯反演。

下面转自http://blog.csdn.net/regina8023/article/details/43876363(侵删)

 

先把题目转化为求x<=a/d,y<=b/d且gcd(x,y)=1的(x,y)有多少对。

 


 

而莫比乌斯函数有一个性质是

 


 

这里的n=1与gcd(x,y)=1相似。

 


 

(下面引用自 iwtwiioi)

 

 

 


 


 

如果直接枚举d来做会TLE,但是我们发现a'/d的值在d等于好多值得时候都是相同的。

 


 

比如a'=100,那么d在[34,50]之间a'/d都是2。

 


 

那么我们可以把连续的一段d一起来算(分块):

 


 

设a'/d=x,那么最后一个a'/d=x的d=a'/x,所以这段连续的区间就是[d,a'/(a'/d)]

 


 

结合b'/d,取个min就可以了。

 

 

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #define min(a, b) ((a) < (b) ? (a) : (b))
 6 
 7 inline void read(long long &x)
 8 {
 9     x = 0;char ch = getchar(), c = ch;
10     while(ch < '0' || ch > '9')c = ch, ch = getchar();
11     while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();
12     if(c == '-')x = -x;
13 }
14 
15 const long long MAXN = 50000 + 10;
16 
17 long long miu[MAXN << 1], t, n, m, k, bprime[MAXN << 1], prime[MAXN << 1], tot, v;
18 
19 void make_miu()
20 {
21     miu[1] = 1;
22     for(register long long i = 2;i <= MAXN;++ i)
23     {
24         if(!bprime[i])
25         {
26             prime[++tot] = i;
27             miu[i] = -1;
28         }
29         for(register long long j = 1;j <= tot && i * prime[j] <= MAXN;++ j)
30         {
31             bprime[i * prime[j]] = 1;
32             if(i % prime[j] == 0)
33             {
34                 miu[i * prime[j]] = 0;
35                 break;
36             }
37             miu[i * prime[j]] = -miu[i];
38         }
39     }
40     for(register long long i = 1;i <= MAXN;++ i)miu[i] += miu[i - 1];
41 }
42 
43 int main()
44 {
45     read(t);
46     make_miu();
47     for(;t;-- t)
48     {
49         read(n), read(m), read(k);
50         n /= k, m /= k;
51         long long ma = min(n, m);
52         register long long ans = 0, x, y, end; 
53         for(register long long d = 1;d <= ma;++ d)
54         {
55             //当前n/d = x  最后一个d = n/x   x:[d, n/(n/x)] 
56             x = n/d, y = m/d;
57             end = min(n/x, min(m/y, ma));
58             ans += (miu[end] - miu[d - 1]) *  x * y;
59             d = end;
60         }
61         printf("%lld\n", ans);
62     }
63     return 0;
64 }
BZOJ1101

 

posted @ 2017-09-06 16:12  嘒彼小星  阅读(142)  评论(0编辑  收藏  举报