Forever Young

洛谷 P2158 [SDOI2008]仪仗队

洛谷 P2158 [SDOI2008]仪仗队

思路

斜率+求欧拉函数

将左下角第一个点看做\((0,0)\),由于正方形关于对角线对称,所以只考虑由对角线分割开来的一侧的三角,最后再乘以二即可,然后如果作图我们就可以发现,同在一条直线上的点只能看见一个,也就是说同在一条直线上的点会被这条直线上第一个点所遮挡,所以如果被遮挡的话他们所在直线的斜率一定相等(画图看一下同一条直线上点的斜率即可),假如一条直线上的某一个点是\((x,y)\),那这条直线的斜率就是\(k = \frac{y}{x}\),这样我们就可以知道这条直线上的第一个点,假设这个点是\((x_0,y_0)\),那么\(\gcd(x_0,y_0)=1\),这条直线上后面的点\((x_0 * n,y_0 * n)\)都会被这个点所遮挡,所以问题就可以转化为求

\[\sum\limits_{i = 2}^{n - 1}\sum\limits_{j = 1}^{i}\gcd(i,j) = 1 \]

显然后面的\(\sum\limits_{j = 1}^{i}\gcd(i,j) = 1\)就是求\(i\)的欧拉函数\(\phi(i)\),所以最后我们要求

\[ans = \sum\limits_{i = 2}^{n - 1}\phi(i) \]

因为我们只求了一个三角形,所以要\(*2\),但是这还不够,因为\((0,1),(1,0),(1,1)\)并没有算进去,所以还要\(+3\),所以最后的答案就是\(ans * 2 + 3\),注意特判一下\(n=1\)的情况,这种情况下是\(0\)

代码

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, cnt, maxn, phi[A], c[A], sum[A], p[A];
bool vis[A];

inline void getphi() {
	phi[1] = 1;
	for (int i = 2; i <= maxn + 1; i++) {
		if (!vis[i]) phi[i] = i - 1, p[++cnt] = i;
		for (int j = 1; j <= cnt && i * p[j] <= maxn + 1; j++) {
			vis[i * p[j]] = 1;
			if (i % p[j] == 0)  phi[i * p[j]] = phi[i] * p[j];
			else phi[i * p[j]] = phi[i] * (p[j] - 1);
		}
	}
	sum[2] = phi[2];
	for (int i = 3; i <= maxn + 1; i++) sum[i] = sum[i - 1] + phi[i];
}

signed main() {
	maxn = n = read();
	getphi();
	if (n == 1) return puts("0"), 0;
	cout << sum[n - 1] * 2 + 3 << '\n';
	return 0;
}

posted @ 2020-04-17 16:53  Loceaner  阅读(108)  评论(8编辑  收藏  举报