SDOI2008 仪仗队

题目描述 Description

  作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

  现在,C君希望你告诉他队伍整齐时能看到的学生人数。

 

 

输入描述 Input Description

  共一个数N。

输出描述 Output Description

  共一个数,即C君应看到的学生人数。

样例输入 Sample Input

4

样例输出 Sample Output

9

 

数据范围及提示 Data Size & Hint

对于 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 }            

 

 

  

  

posted @ 2015-03-17 20:52  Xstsow  阅读(218)  评论(0编辑  收藏  举报