欧拉函数专题
浅谈欧拉函数
定义:
φ(n)表示小于n的正整数中和n互质的个数;
性质:
1.积性函数:φ(n×m)=φ(n)×φ(m)(感性理解)
2.aφ(n)≡1(mod n),当且仅当gcd(a,n)==1(感性理解)
3.[1,n]中与n互质的数的和为n×φ(n)/2
4.Σφ(d)=n,其中(d|n)(感性理解)
5.φ(pa)=pa-pa-1,其中(p为素数,a为正整数)
证明:
这里插入个游戏:
问题:求正整数383的最后两位数
回到正题
一:√n求单个数的欧拉函数值:、
根据性质五:
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1e7 + 10;
int p, ans = 1, N;
void GetPhi() {
for(int i = 2; i * i <= p; i++) {
if(p % i == 0) {
int now = i - 1; p /= i;
while(p % i == 0) now = now * i, p /= i;
ans = ans * now;
}
}
if(p != 1) ans *= (p - 1);
}
int main() {
cin >> p; N = p;
GetPhi();
cout << ans;
return 0;
}
二:线性塞欧拉函数
以下就是用到的性质
性质一:
φ(p)=p-1,当且仅当p为素数时
性质二:
若p不为i的约数且p为素数
则φ(i∗p)=φ(i)∗φ(p)
=φ(i∗p)=φ(i)∗(p−1)
这一步同时利用了性质1和欧拉函数的积性
性质3
若p是i的约数且p为素数,
则φ(i∗p)=φ(i)∗p
这个根据最开始的性质五可以得到
part code by wzxbeliever:
void init(){
phi[1]=1;
for(ri i=2;i<=n;i++){
if(!vis[i])prime[++tot]=i,phi[i]=i-1;
for(ri j=1;j<=tot&&i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(!(i%prime[j])){phi[i*prime[j]]=phi[i]*prime[j];break;}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
光说不用等于放屁
例题一:
https://www.luogu.org/problem/P2158
分析:
由图观察发现能被看到的点当且仅当gcd(i,j)==1,
注意看图的时候将两点间空格看成一格,不要将点看成一格
所以问题转化为互质的点对有多少个
这里很容易想到欧拉函数
因为两边是对称的,只算一边就好了
code by wzxbeliever:
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=40005;
int n,tot;
ll ans;
bool vis[maxn];
int phi[maxn],prime[maxn];
il void init(){
phi[1]=1;
for(ri i=2;i<=n;i++){
if(!vis[i])prime[++tot]=i,phi[i]=i-1;
for(ri j=1;j<=tot&&i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(!(i%prime[j])){phi[i*prime[j]]=phi[i]*prime[j];break;}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int main(){
scanf("%d",&n);
if(n==1){printf("0\n");return 0;}//注意特判0
init();
for(ri i=1;i<=n-1;i++)
ans+=phi[i];
ans<<=1;ans++;
printf("%lld\n",ans);
return 0;
}
http://poj.org/problem?id=2480
题意:求 Σgcd(i,N) 其中 1<=i<=N ,N<=1e9
分析:
考虑每一个N的因子d 如果gcd(m,n)=d 那我们需要找出有多少这样的 m
即为 d×phi(N/d)
也许你会想 为什么不是 n/d 个呢 应该有 n/d 个都是 d 的倍数,拥有 d 这个因子
考虑如果与 n/d 不互质的一个数乘以d 和 n 的 gcd 一定不是 d 那么就会重复计算
注意特判 k=i 的情况
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long LL;
LL gao( LL sum, LL k, LL p)
{
LL ans = 0;
LL t = sum;
ans += k*(t - t/p);
ans += t;
return ans;
}
int main()
{
LL n;
while(scanf("%I64d",&n)!=EOF){
LL t = n; LL ans = 1;
for( LL i = 2;i*i<=t;i++){
if(t%i) continue;
LL cnt =0; LL sum = 1;
while(t%i==0){
t/=i;cnt++;sum*=i;
}
ans *= gao(sum,cnt,i);
}
if(t>1) ans*=gao(t,1,t);
printf("%I64d\n",ans);
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=4983
分析:
当k>=2 的时候 很明显不成立
注意n=1的情况特判
当k=1 时 即gcd(a,n)=x、gcd(b,n)=n/x时个数,因为a<=n,所以gcd(a,n)的个数=u[n/x],u是欧拉函数。所以原式等于 Σ(u[n/x]*u[x]) 其中 x|n
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MOD 1000000007
#define ll long long
ll eular(ll n)
{
ll res=1;
for(ll i=2;i*i<=n;i++)
{
if(n%i==0)
{
n/=i,res*=i-1;
while(n%i==0)
{
n/=i;
res*=i;
}
}
}
if(n>1) res*=n-1;
return res;
}
ll n,k;
int main()
{
while(scanf("%I64d%I64d",&n,&k)==2)
{
if(k==2 || n==1)
{
printf("1\n");
continue;
}
if(k>2)
{
printf("0\n");
continue;
}
ll ans=0;
for(ll i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(i*i!=n)
ans=(ans+eular(n/i)*eular(i)*2)%MOD;
else
ans=(ans+eular(n/i)*eular(i))%MOD;
}
}
printf("%I64d\n",ans);
}
return 0;
}
这个题目数据范围1e6 可以考虑每个因数 如果数据范围大点的话 就要用到上面的方法
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define inf 1000005
#define ll __int64
int p[inf];
ll f[inf], sum[inf];
void is_prime()
{
memset(p, 0, sizeof(p));
p[1] = 1;
for(int i = 2; i < inf; i++)
{
if(p[i] == 0)
{
for(int j = i; j < inf; j+=i)
{
if(p[j] == 0) p[j] = j;
p[j] = p[j]/i*(i-1);
}
}
}
}
int main()
{
is_prime();
memset(f, 0, sizeof(f));
f[1] = 0;
for(int i = 1; i < inf; i++)
{
for(int j = 2*i; j < inf; j+=i)
{
f[j] += (ll)p[j/i]*i;
}
}
sum[1] = 0;
for(int i = 2; i < inf; i++) sum[i] = sum[i-1] + f[i];
int n;
while(~scanf("%d", &n))
{
if(n == 0) break;
printf("%I64d\n", sum[n]);
}
return 0;
}
https://blog.nowcoder.net/n/313966e56e3f497990a8530ac9d40660
#include<cctype>
#include<cstdio>
#include<algorithm>
#define F inline
using namespace std;
typedef long long LL;
const int N=1e7+5;
int t,n,p[N]; LL phi[N],ans[5005];
bool f[N];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F void Make(int n){
phi[1]=1;
for (int i=2;i<=n;i++){
if (!f[i]) p[++p[0]]=i,phi[i]=i-1;
for (int j=1,v;j<=p[0]&&i*p[j]<=n;j++){
f[v=i*p[j]]=true;
if (i%p[j]==0){ phi[v]=phi[i]*p[j]; break; }
phi[v]=phi[i]*phi[p[j]];
}
}
for (int i=2;i<=n;i++) phi[i]+=phi[i-1];
}
F LL calc(int n){
LL ans=0;
for (LL l=1,r;l<=n;l=r+1)
r=n/(n/l),ans+=(phi[r]-phi[l-1])*((phi[n/l]<<1)-1);
return ans;
}
#define max(x,y) ((x)>(y)?(x):(y))
int main(){
t=_read(); int mx=0,tmp=t;
for (int i=1;i<=t;i++)
ans[i]=_read(),mx=max(mx,ans[i]);
for (Make(mx);t;t--) ans[t]=calc(ans[t]);
for (int i=1;i<=tmp;i++) printf("%lld\n",ans[i]);
return 0;
}