UVA Tress in a Wood

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\n",fin);
    }
    return 0;
}

 

posted @ 2017-07-27 10:25  猪突猛进!!!  阅读(120)  评论(0编辑  收藏  举报