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

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。
输入格式
一行,一个正整数 N。
输出格式
输出一行一个数,即 C 君应看到的学生人数。
输入输出样例
输入 #1
4
输出 #1
9
说明/提示
对于100%的数据,\(1≤N≤40000\)。
思路分析:
首先我们可以将题目转化为直角坐标系上求直线斜率个数。
设左下角坐标为\((0,0)\),其余点为\((x,y)\)
因为n从0开始,所以我们假设方阵的边长为n-1
对于每条直线\(y = kx\), 斜率\(k = \frac{y}{x}\),我们可以发现当gcd(y, x) = 1时,k的数量便可加1。简而言之,k的个数等价于1到n-1,互质的x-y对个数,由于方阵关于y = x对称,因此只需要算一半,最后将答案乘2即可。至于计算方法是欧拉函数的裸题。同时不要忘记加上x = 0 和 y = 0 时的两个人。
PS: x 和 y从零开始是为了更好的设直线方程,有助于理解。 但题中的序号是从1开始的。 因此计算k的个数时是计算2 到 n的欧拉函数和
代码示例:
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
int phi[40005];
int isprime[40005];
int prime[40005];
int cnt;
int ans;
int n;
int main ()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
if(n == 1) //n = 1时特别处理
{
cout << 0 << endl;
return 0;
}
phi[1] = 1;
For(i, 2, n) //欧拉线性筛,具体可以看另一篇博客
{
if(!isprime[i]) prime[++cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && i * prime[j] <= n; j++)
{
isprime[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
n--;
For(i, 2, n) ans += phi[i]; //欧拉函数求和
ans = (ans + 1) * 2; //不要忘记第一行和第一列的两个人
cout << ++ans << endl;
return 0;
}
/*
*/

浙公网安备 33010602011771号