Luogu P2158 仪仗队 题解报告

题目传送门

【题目大意】

给定一个n×n的点方阵,求站在左下角的点能看到的点数

注意同一条直线上只能看到一个点

【思路分析】

因为是一个方阵,所以可以对称地算,那么对于半个方阵,这里假设是左上的半个方阵,能看到的点的个数要满足这样的条件

1.x<y

因为是左上的半个方阵,并且x=y的一直线上的点要额外计算

2.gcd(x,y)即x与y互质

这是为了保证一直线上只能看到一个点

容易发现,在满足条件的情况下,这样的x个数恰好等于φ(y)

还需要注意的一点是,最左边一列,最下面一行,还有x=y这条直线上一共可以看到三个点,所以要额外计算

于是最后的答案Ans=3+2*$sum_{i=2}^{n}varphi[i]$

【代码实现】

 1 #include<bits/stdc++.h>
 2 #define go(i,a,b) for(register int i=a;i<=b;i++)
 3 using namespace std;
 4 const int N=40002;
 5 int v[N],prime[N],phi[N];
 6 int n;
 7 int fr(){
 8     int w=0,q=1;
 9     char ch=getchar();
10     while(ch<'0'||ch>'9'){
11         if(ch=='-') q=-1;
12         ch=getchar();
13     }
14     while(ch>='0'&&ch<='9')
15         w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
16     return w*q;
17 }
18 void work(){
19     memset(v,0,sizeof(v));
20     int num=0;
21     go(i,2,n){
22         if(v[i]==0){//如果i没有被标记过,那就是质数
23             v[i]=i,prime[++num]=i;
24             phi[i]=i-1;//性质2
25         }
26         go(j,1,num){
27             if(prime[j]>v[i]||prime[j]>n/i) break;
28             v[i*prime[j]]=prime[j];
29             phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
30             //性质8和性质9的结合
31         }
32     }
33 }
34 int ans=0;
35 int main(){
36     n=fr();n--;
37 //这里要注意一下题目的bug,你可以认为输入的是点数但实际上是看格子
38     if(n==0) {printf("0
");return 0;}
39     work();
40     go(i,2,n)
41         ans+=phi[i];
42     ans*=2;ans+=3;
43     printf("%d
",ans);
44     return 0;
45 }
AC代码戳这里
原文地址:https://www.cnblogs.com/THWZF/p/10478999.html