P2158 [SDOI2008]仪仗队

传送门

可以发现图是对称的

所以我们先只考虑下半部分,不包括y=x的点

如果能算出下半部分总和为ans

那么答案就是 ans*2+1(加上y=x的方向有一个同学)

以观察者为原点,建立直角坐标系:

 

那么下半部分的视线的斜率≥0且<1,ans就是不同的斜率数量

从左到右,从下到上考虑每个点(x,y)

它能对答案有贡献当且仅当 x,y,互质

因为如果 x,y 不互质,从原点看x,y的视线的斜率就是x/y= (x/gcd(x,y)) / (y/gcd(x,y))

因为点(x/gcd(x,y)  ,   y/gcd(x,y)) 的点之前已经被考虑过了

所以从原点到(x,y)的视线的斜率也被考虑过了,所以点(x,y)没有对答案产生贡献

如果x,y互质说明这一个斜率之前还没有被其他点"使用"(十分显然)

那就是求 1~n 中每一个小于 n 数 x      的小于x的与 x 互质的数的个数

就是求 1~n 中每一个小于 n 数 x的欧拉函数和

直接线性求出所有欧拉函数值就好了:

注意特判n=1的情况,因为观察者看不到自己所以答案为0

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=1e5+7;
int pri[N],cnt,phi[N];
bool not_pri[N];
int n,ans;
void pre()
{
    not_pri[1]=phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!not_pri[i]) { pri[++cnt]=i; phi[i]=i-1; }
        for(int j=1;j<=cnt;j++)
        {
            int g=i*pri[j];
            if(g>n) break;
            not_pri[g]=1;
            if(!(i%pri[j])) { phi[g]=phi[i]*pri[j]; break; }
            phi[g]=phi[i]*phi[pri[j]];
        }
    }
}
int main()
{
    scanf("%d",&n);
    if(n==1) { printf("0"); return 0; }//特判
    pre();
    for(int i=1;i<n;i++) ans+=phi[i];
    printf("%d",ans*2+1);
    return 0;
}

 

posted @ 2018-10-12 18:56  LLTYYC  阅读(224)  评论(0编辑  收藏  举报