森林里的数Trees in a Wood【约分+欧拉函数】

传送门
显然4个坐标轴上各只能看见一棵树,所以可以只数第一象限 ( 即 x > 0 , y > 0 (即x>0,y>0 (x>0y>0),答案乘 以4后加4。第一象限的所有x, y都是正整数,能看到 ( x , y ) (x,y) (x,y),当且仅当 g c d ( x , y ) = 1 gcd(x,y)=1 gcd(x,y)=1
由于a范围比较小,b范围比较大,一列一列统计比较快。第x列能看到的树的个数等于 0 1≤y≤x:有phi(x)个,这是欧拉函数的定义。 x+1≤y≤2x:也有 p h i ( x ) phi(x) phi(x)个,因为 g c d ( x + i , x ) = g c d ( x , i ) gcd(x+i,x)=gcd(x,i) gcd(x+i,x)=gcd(x,i) 2 x + 1 ≤ y ≤ 3 x 2x+1≤y≤3x 2x+1y3x:也有 p h i ( x ) phi(x) phi(x)个,因为 g c d ( 2 x + i , x ) = g c d ( x , i ) gcd(2x+i,x)=gcd(x,i) gcd(2x+i,x)=gcd(x,i)。 …… k x + 1 ≤ y ≤ b kx+1≤y≤b kx+1yb:直接统计,需要O(x)时间。
换句话说,每次需要计算phi(x)和进行O(x)次直接判断,计算 p h i ( x ) phi(x) phi(x)需要 O ( x 1 2 ) O(x\frac{1}{2}) O(x21)时间,而 直接判断只需要 O ( 1 ) O(1) O(1)时间。再加上枚举x的所有a种可能,总时间为 O ( a 2 ) O(a^2) O(a2)
code:

#include
#include
#include 
using namespace std;
#define ll long long
int phi(int n){
	int ans=n;
	int m=sqrt(n+0.5);
	for(int i=2;i<=m;i++)if(n%i==0){
		ans=ans/i*(i-1);
		while(n%i==0) n/=i;
	}
	if(n>1) ans=ans/n*(n-1);
	return ans; 
}

int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

ll f(int a,int b){
	ll ans=0;
	for(int x=1;x<=a;x++){
		int k=b/x;
		ans+=phi(x)*k;
		for(int y=k*x+1;y<=b;y++){
			if(gcd(x,y)==1) ans++;
		}
	}
	return ans*4+4;
}

int main(){
	int a,b;
	while(cin>>a>>b,a){
		ll K=f(a,b);
		ll N=(ll) (2*a+1)*(2*b+1)-1;
		printf("%.7lf\n",(double)K/N); 
	}
}

你可能感兴趣的:(欧拉函数,约分)