欧拉函数专题

浅谈欧拉函数

定义:

φ(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;
}
posted @ 2019-11-05 16:30  wzx_believer  阅读(248)  评论(0编辑  收藏  举报