UVa 10214 - Trees in a Wood.(欧拉函数)
链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1155
题意:
在满足|x|≤a,|y|≤b(a≤2000,b≤2000000)的网格中,除了原点之外的整点(即x,y坐标均为整数的点)各种着一棵树。
树的半径可以忽略不计,但是可以相互遮挡。求从原点能看到多少棵树。
设这个值为K,要求输出K/N,其中N为网格中树的总数。
分析:
显然4个坐标轴上各只能看见一棵树,所以可以只数第一象限(即x>0,y>0),答案乘以4后加4。
第一象限的所有x, y都是正整数,能看到(x,y),当且仅当gcd(x,y)=1。
由于a范围比较小,b范围比较大,一列一列统计比较快。
第x列能看到的树的个数等于0<y≤b的数中满足gcd(x,y)=1的y的个数。可以分区间计算。
1≤y≤x:有phi(x)个,这是欧拉函数的定义。
x+1≤y≤2x:也有phi(x)个,因为gcd(x+i,x)=gcd(x,i)。
2x+1≤y≤3x:也有phi(x)个,因为gcd(2x+i,x)=gcd(x,i)。
……
kx+1≤y≤b:直接统计,需要O(x)时间。
代码:
1 import java.io.*; 2 import java.util.*; 3 4 public class Main { 5 Scanner cin = new Scanner(new BufferedInputStream(System.in)); 6 final int UP = 2000 + 5; 7 int a, b, phi[] = new int[UP]; 8 9 void constant() { 10 phi[1] = 1; 11 for(int i = 2; i < UP; i++) phi[i] = 0; 12 for(int i = 2; i < UP; i++) if(phi[i] == 0) { 13 for(int t = i; t < UP; t += i) { 14 if(phi[t] == 0) phi[t] = t; 15 phi[t] = phi[t] / i * (i-1); 16 } 17 } 18 } 19 20 int gcd(int a, int b) { 21 return b == 0 ? a : gcd(b, a%b); 22 } 23 24 long black() { 25 long res = 0; 26 for(int i = 1; i <= a; i++) { 27 int k = b / i; 28 res += k * phi[i]; 29 for(int t = k*i+1; t <= b; t++) { 30 if(gcd(i,t) == 1) res++; 31 } 32 } 33 return res * 4 + 4; 34 } 35 36 void MAIN() { 37 constant(); // 预处理欧拉函数值 38 while(true) { 39 a = cin.nextInt(); 40 b = cin.nextInt(); 41 if(a + b == 0) break; 42 long all = (2*a+1L) * (2*b+1L) - 1; 43 System.out.printf("%.7f\n", (double)black() / all); 44 } 45 } 46 47 public static void main(String args[]) { new Main().MAIN(); } 48 }