hdoj 2588 GCD 【欧拉函数 问题转换】
GCD
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1205 Accepted Submission(s): 546
Problem Description
The greatest common divisor GCD(a,b) of two positive integers a and b,sometimes written (a,b),is the largest divisor common to a and b,For example,(1,2)=1,(12,18)=6.
(a,b) can be easily found by the Euclidean algorithm. Now Carp is considering a little more difficult problem:
Given integers N and M, how many integer X satisfies 1<=X<=N and (X,N)>=M.
(a,b) can be easily found by the Euclidean algorithm. Now Carp is considering a little more difficult problem:
Given integers N and M, how many integer X satisfies 1<=X<=N and (X,N)>=M.
Input
The first line of input is an integer T(T<=100) representing the number of test cases. The following T lines each contains two numbers N and M (2<=N<=1000000000, 1<=M<=N), representing a test case.
Output
For each test case,output the answer on a single line.
Sample Input
3 1 1 10 2 10000 72
Sample Output
1 6 260
题意: 在区间[1,n] 里面求出满足 gcd(x, n) >= m 的x有多少个。
转换问题: 我们可以让 n = a * b,x = a * d(b >= d),若gcd(x, n) = a ,那么 b 和 d 一定互质 ,而此时 x的个数即 b 的欧拉函数。
这样问题就变成 在满足 b >= m 的情况下 , 对应n / b 的欧拉函数之和。
结果果断TLE。。。
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- int euler(int n)//求n的欧拉函数
- {
- int eu = n;
- int i;
- for(i = 2; i*i <= n; i++)
- {
- if(n % i == 0)
- {
- eu = eu * (i-1) / i;
- while(n % i == 0)
- n /= i;
- }
- }
- if(n > 1) eu = eu * (n-1) / n;
- return eu;
- }
- int main()
- {
- int t;
- int n, m;
- int i;
- int ans;
- scanf("%d", &t);
- while(t--)
- {
- scanf("%d%d", &n, &m);
- ans = 0;
- for(i = 1; i <= n; i++)//遍历所有可能的 i
- {
- if(n % i)//不能整除
- continue;
- /*只处理n % i == 0 的 i */
- if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
- }
- printf("%d\n", ans);
- }
- return 0;
- }
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int euler(int n)//求n的欧拉函数
{
int eu = n;
int i;
for(i = 2; i*i <= n; i++)
{
if(n % i == 0)
{
eu = eu * (i-1) / i;
while(n % i == 0)
n /= i;
}
}
if(n > 1) eu = eu * (n-1) / n;
return eu;
}
int main()
{
int t;
int n, m;
int i;
int ans;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
ans = 0;
for(i = 1; i <= n; i++)//遍历所有可能的 i
{
if(n % i)//不能整除
continue;
/*只处理n % i == 0 的 i */
if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
}
printf("%d\n", ans);
}
return 0;
}
问题出在哪? 很显然,
- for(i = 1; i <= n; i++)//遍历所有可能的 i
- {
- if(n % i)//不能整除
- continue;
- /*只处理n % i == 0 的 i */
- if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
- }
for(i = 1; i <= n; i++)//遍历所有可能的 i
{
if(n % i)//不能整除
continue;
/*只处理n % i == 0 的 i */
if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
}
n最大1000 000 000 TLE必然事件。
可以这样优化一下:
只遍历一半,加上 n/i >= m 的情况(记着i * i == n 的情况不能加)
- for(i = 1; i*i <= n; i++)//遍历所有可能的 i
- {
- if(n % i)//不能整除
- continue;
- /*只处理n % i == 0 的 i */
- if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
- if(n/i >= m && i*i != n) ans += euler(i);
- }
for(i = 1; i*i <= n; i++)//遍历所有可能的 i
{
if(n % i)//不能整除
continue;
/*只处理n % i == 0 的 i */
if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
if(n/i >= m && i*i != n) ans += euler(i);
}
ac代码:15ms (真快)
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- int euler(int n)//求n的欧拉函数
- {
- int eu = n;
- int i;
- for(i = 2; i*i <= n; i++)
- {
- if(n % i == 0)
- {
- eu = eu * (i-1) / i;
- while(n % i == 0)
- n /= i;
- }
- }
- if(n > 1) eu = eu * (n-1) / n;
- return eu;
- }
- int main()
- {
- int t;
- int n, m;
- int i;
- int ans;
- scanf("%d", &t);
- while(t--)
- {
- scanf("%d%d", &n, &m);
- ans = 0;
- for(i = 1; i*i <= n; i++)//遍历所有可能的 i
- {
- if(n % i)//不能整除
- continue;
- /*只处理n % i == 0 的 i */
- if(i >= m) ans += euler(n/i);//大于或者等于 m 加上对应的n/i的欧拉函数
- if(n/i >= m && i*i != n) ans += euler(i);
- }
- printf("%d\n", ans);
- }
- return 0;
- }