https://vjudge.net/problem/UVA-10214
题意:给定一个坐标系。|x|<=a, |y|<=b 求坐标系中有多少点是可以从原点直接看到(即从原点和一个点连线,线段没有经过其他点就ok);
题解:由于是和原点连线,所以所有的点满足y=k*x;我们可以发现满足条件的点是坐标互素的点。用反证法证明,假设一个点的x,y左边不互素,即gcd(x,y)!=1,y=k*x,则必然存在一个点y0=y/g,x0=x/g 也满足y0=k*x0,而且x0,y0,势必相对于x,y而言更加靠近原点,所以x,y是不符合条件的。但是由于x,y的大小范围不一致,不好通过欧拉直接枚举怎么处理呢?gcd(n,m)=gcd(n+m,n) 这个东西就用上了
对于n 1~n中的x gcd(x,n)=1的个数为phi(n);
同样对于n 1+n~n+n中的x gcd(x,n)=1的个数与phi(n)一致!
这样我们就可以枚举范围小的值,然后多出来的部分单独处理
ac代码:
#include <cstdio> #include <iostream> #include <cstring> #include <queue> using namespace std; typedef long long ll; int phi[2001]; void init() { for(ll i=1;i<=2000;i++) phi[i]=i; for(ll i=2;i<=2000;i++) { if(phi[i]==i) { for(ll j=i;j<=2000;j+=i) { phi[j]=phi[j]/i*(i-1); } } } } ll gcd(ll a,ll b) { if(b==0) return a; else return gcd(b,a%b); } int main() { ll a,b; init(); while(cin>>a>>b) { if(a==0 && b==0) break; ll minn=min(a,b); ll maxx=max(a,b); ll sum=0; ll k; for(ll i=1;i<=minn;i++) { ll temp=phi[i]; ll ret=maxx/i; temp*=ret; for(ll j=1;j<=maxx%i;j++) { if(gcd(j,i)==1) temp++; } // cout<<temp<<endl; sum+=temp; } sum=sum*4+4; k=(2*a+1)*(2*b+1)-1; double fin=(sum*1.0)/(k*1.0); printf("%.7lf ",fin); } return 0; }