P2158 仪仗队

题目

给一个由学生组成的 $n*n$ 的方阵,求在一个角可以看到的学生人数。

思路:

我们将左下角的位置标为 $(0,0)$,向右为 x 正方向,向上为 y 正方向。

img

规律的寻找和证明

围绕着角的三个点,即 $(1,0)$, $(1,1)$, $(0,1)$ 显然都可以被看到,而对于其他的点来说,某个点可以被看到当且仅当 x y 互质。

我们可以做以下证明:取某一个位置并与原点连线,形成的直线是 $y=kx$。如果这个点 x y 不互质,则可以同时除以它们的最大公约数 p,得到 $\frac{x}{p}=k\frac{y}{p}$——显然$(\frac{y}{p},\frac{x}{p})$ 也在直线 $y=kx$ 上而且距离原点更近。这样就导致在看到 $(x,y)$ 之前先看到了 $(\frac{y}{p},\frac{x}{p})$,这样 $(x,y)$ 就看不到了。

题目的转换

因为方阵关于对角线对称,其左上和右下可以看到的点数是相同的,所以只需要求其中的一侧,最后再乘以二就可以了。我们现在来看对角线左上的点:根据之前所得到的性质,我们要找出所有 x y 互质的点的个数。对角线左上的点都有 $x<y$,那么对于每一行就是要求比 y 小而且与 y 互质的点的个数——是不是听着耳熟?这不是就是欧拉函数 $\varphi(y)$? 于是我们就将原题变成一个计算题:$Answer=3+2*\Sigma^{n}_{i=2}\varphi(i)$

欧拉函数的计算

计算欧拉函数,我们需要使用它的两条性质:

  • $p$ 为素数,若 $p\mid n$ 且 $p^2\mid n$,则有 $\varphi(n)=\varphi(n/p)*p$
  • $p$ 为素数,若 $p\mid n$ 但 $p^2\nmid n$,则有 $\varphi(n)=\varphi(n/p)*(p-1)$

我们可以利用线性筛法每个合数 $n$ 只被其最小素因子 $p$ 筛一次的特点(不会线性筛法?),将原公式变为 $\varphi(n*p)=\varphi(n)p$ 和 $\varphi(np)=\varphi(n)*p$。至此,我们有了求欧拉函数的方法,这道题也就迎刃而解了。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MaxN = 40000 + 5;
int n, answer;
int phi[MaxN], minimizedPrimeFactor[MaxN], primeNumber[MaxN], primeCount;
void Eular(int n){
	memset(minimizedPrimeFactor,0,sizeof(minimizedPrimeFactor));
	primeCount = 0;
	for(int i = 2; i <= n; i++){
		if(minimizedPrimeFactor[i] == 0){
			minimizedPrimeFactor[i] = i;
			primeNumber[++primeCount] = i;
			phi[i] = i-1;
		}
		for(int j = 1; j <= primeCount; j++){
			if(primeNumber[j] > minimizedPrimeFactor[i] || primeNumber[j] > n/i) break;
			minimizedPrimeFactor[i * primeNumber[j]] = primeNumber[j];
			phi[i * primeNumber[j]] = phi[i] * (i % primeNumber[j]? primeNumber[j] - 1 : primeNumber[j]); 
            //注意这里三目运算结果的顺序
		}
	}
	return;
}
int main(){
	scanf("%d",&n);
	if(n == 1){
		printf("0");
		return 0;
	}
	Eular(n);
	for(int i = 2; i < n; i++) answer+=phi[i];
	printf("%d\n",answer*2+3);
	return 0;
}

为什么只需要i % primeNumber[j]? 因为 i 是合数,已经有一个因子是 primeNumber[j]了。

值得注意的是三目运算时两个结果的顺序:取模运算结果为 0 是整除,对应第一条性质;结果为 1 是不整除,对应第二条性质(我才不会说第一遍写错而且看了好久也没看出来QWQ)。

Sorry potatoler, but I cannot recognize you.
Built with Hugo
Theme Stack designed by Jimmy