P2158 [SDOI2008]仪仗队(欧拉函数)

Aimee

很显然,对于每一行来讲,如果他们\(gcd(x,y)\)不为1,那么这个斜率一定出现过,所以说呢,需要排除

再结合一下对称性,可以知道对于每一列,可用的点对就是\(\psi(x)\)

最后的答案就是\(3+2*\sum_{i=2}^{n-1}\psi(i)\)

为什么是n-1呢,因为体委再(1,1)处

那么问题来了,怎么求欧拉函数

欧拉函数

首先我们有\(\psi(n)=n*\prod_{prime \quad p|n}(1-\frac{1}{p})\)

这个东西是怎么来的呢,考虑一下算数基本定理,对于任意两个质因数p,q,在1~n中分别有\(\frac{n}{p}\) \(\frac{n}{q}\)

个他们的倍数,减去他们,但是也要去掉多减的pq部分,就会有

\[n-\frac{n}{p}-\frac{n}{q}+\frac{n}{p*q}=n*(1-\frac{n}{p})(\frac{n}{q}) \]

这样就可以计算了,这是个积性函数

积性函数

\[f(ab)=f(a)(b)\quad gcd(a,b)=1 \]

欧拉函数的一些性质

对于指数p 若\(p|n \ \&\ p^2|n\)那么有\(\psi(n)=\psi(n/p)*p\)

对于指数p 若\(p|n \ \&\ n\%p^2\neq0\)那么有\(\psi(n)=\psi(n/p)*p\)

不使用这些性质,可以搞出改造后的埃氏筛 \(O(n\log n)\)做法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int phi[400001];
int znx;
int main(){
	scanf("%d",&n);
	for(int i=2;i<=n;++i) phi[i]=i;
	for(int i=2;i<=n;++i){
		if(phi[i]==i){
			for(int j=i;j<=n;j+=i){
				phi[j]=phi[j]/i*(i-1);
			} 
		}
		if(i!=n)
		znx+=phi[i]+phi[i]; 
	} 
	if(n==1){
		cout<<0;
	}else
	cout<<znx+3;
	return 0;
}

但是完全可以搞成线性的,利用上面提到的性质,改造欧拉筛

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int phi[400001];
int znx;
int prime[400001];
int p;
int v[400001];
int main(){
	scanf("%d",&n);
	for(int i=2;i<=n;++i) phi[i]=i;
	for(int i=2;i<=n;++i){
		if(v[i]==0){
			v[i]=i;
			prime[++p]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=p;++j){
			if(prime[j]>v[i]||prime[j]>n/i) break;
			v[i*prime[j]]=prime[j];
			phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
		}
		if(i!=n)
		znx+=phi[i]+phi[i]; 
	} 
	if(n==1){
		cout<<0;
	}else
	cout<<znx+3;
	return 0;
}

和平常的似乎不太一样啊
break呢?
没错,它慢了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int phi[400001];
int znx;
int prime[400001];
int p;
int v[400001];
int main(){
	scanf("%d",&n);
	for(int i=2;i<=n;++i) phi[i]=i;
	for(int i=2;i<=n;++i){
		if(v[i]==0){
			v[i]=i;
			prime[++p]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=p;++j){
			if(prime[j]>v[i]||prime[j]>n/i) break;
			v[i*prime[j]]=prime[j];
			phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
			if(i%prime[i]==0)
			break;
		}
		if(i!=n)
		znx+=phi[i]+phi[i]; 
	} 
	if(n==1){
		cout<<0;
	}else
	cout<<znx+3;
	return 0;
}
posted @ 2021-06-20 17:24  Simex  阅读(82)  评论(0编辑  收藏  举报