Luogu P2158 [SDOI2008] 仪仗队

P2158 [SDOI2008] 仪仗队

题目描述

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

img

现在,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;
}	
/*

*/
posted @ 2021-11-05 11:43  Yra  阅读(38)  评论(0)    收藏  举报