作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。
现在,C君希望你告诉他队伍整齐时能看到的学生人数。
共一个数N。
共一个数,即C君应看到的学生人数。
4
9
对于 30% 的数据,1≤N≤1000
对于 100% 的数据,1≤N≤40000
一开始这个题目在CODEVS上的样例输出是错的,让我想了半天,直到佣神帮我把数据改过来。
我们可以把最左下角当作(0,0),然后最右上角自然就是(N-1, N-1),想一想就会发现,如果一个点(X,Y)可以被观测到,那么(KX, KY)就无法被观察到,因为从左下角看过去,这两个点在同一条直线上。
还有一点很容易证明的是,这张图沿着左下-右上这条对角线分开,两部分是对称的,一个比较需要注意的是(1,1)并不属于其中任何一部分,而是在对角线上。
把其中一部分删去后,我们就得到了一个三角形,根据前边标红的性质,对于第N行,我们只需要求出小于等于N且和N互质的正数有多少个就好,这时候我们就可以用到欧拉函数来求解。
把小于等于N且和N互质的数的个数设为phi(N),比如phi(8) = 4,因为1,3,5,7和8互质。
phi(x) = x* (1 - 1/p1) * (1 - 1/p2) ... * (1 - 1/pn)
其中的p1到pn分别为x的n个质因数,需要注意的是,每个质因数只被计算一遍,比如12=2*2*3 phi(12) = 12*(1/2)*(2/3) = 4。
求出phi(2)到phi(n - 1)的和并乘二之后,我们需要特别处理第零列和第零行上的两个点和(1,1)这个点,也就是加上3(第零行和第零列的点可以不特殊处理而是将phi(0)设为1)。
代码如下(求不吐槽风格)
1 #include<iostream> 2 using namespace std; 3 4 int ans, n, a[40001]; 5 6 int main() { 7 cin >> n; 8 n = n - 1; 9 for (int i = 1; i <= n; i++) 10 a[i] = i; //PHI(I) 11 for (int i = 2; i <= n; i++) //筛法求phi(i) 12 if (a[i] == i) { 13 int p = 1; 14 while (p * i <= n) { 15 a[p * i] = a[p * i] / i * (i - 1); 16 p++; 17 } 18 } 19 for (int i = 1; i <= n; i++) 20 ans += a[i]; 21 ans = ans * 2 + 1; //(1,1)被计算两次,(0,1)(1,0)没被计算。 22 cout << ans; 23 return 0; 24 }