solution

· · 题解

第一次看到这一题时,觉得是一道数论题,就手推了一下。

0是看不到,1是看得到。

n=1
0

n=2
11
01

n=3
010
111
010

n=4
0110
0101
1111
0100

n=5
01010
01101
01010
11111
01000
……

有没有发现什么?

(没有)

4 0 1 0 1 0
3 0 1 1 0 1
2 0 1 0 1 0
1 1 1 1 1 1
0 0 1 0 0 0
  0 1 2 3 4

如果我们将它标号,会发现所有除第0行或第0列以外,所有能被看到的同学的坐标是互质的,即:

点P(x,y)能被看到
则有(x,y)=1

所以就有了下面的暴力做法

for(int i=1;i<n;++i){
    for(int j=1;j<n;++j){
        if(gcd(i,j)==1) ++ans;
    }
}
ans+=2;

提交,T了,36分。

又得改进这究竟是人性的扭曲,还是道德的沦丧?

所以,根据坐标互质这个特点,我们就有了应用欧拉函数的思路

函数内容:(引用自百度百科)

对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目

我们就可以利用欧拉函数,在O(n)的时间内完成求解

因此,我们只需要求出从1~n的欧拉函数值,就能A掉题目

最终代码:

#include<cstdio>
using namespace std;
const int N=40001;
bool prime[N];
int m[N],phi[N],p[N],nump,n,ans;
void make()
{
    phi[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!m[i])
        {
            p[++nump]=i;
            phi[i]=i-1;
        }
        for (int j=1;j<=nump&&p[j]*i<n;j++)
        {
            m[p[j]*i]=1;
            if (i%p[j]==0)
            {
                phi[p[j]*i]=phi[i]*p[j];
                break;
            }
            else phi[p[j]*i]=phi[i]*(p[j]-1);
        }
    }
}
int main()
{
    scanf("%d",&n);
    if(n==1){
        printf("0");
        return 0;
    }
    make();
    for(int i=2;i<n;++i){
        ans+=phi[i];
    }
    ans*=2;
    ans+=3;
    printf("%d",ans);
    return 0;
}

注意:这里在写的时候使用的是欧拉筛法。如果使用O(n^2)的普通筛法就有可能会TLE