【欧拉函数】(数论)
何为欧拉函数:
在1-N中与N互质的数的个数被称为欧拉函数,记为φ(N).
若在算术基本定理中,N=p1^c1*p2^c2*....pm^cm,则
φ(N)=N*(p1-1)/p1*(p2-1)/p2*****(pm-1)/pm=N*ㄇ质数p|N (1-1/p)
模板:求解单个数的欧拉函数计算式:
ll eluer(ll n)
{ll ans=n;for(ll i=2;i*i<=n;i++){if(n%i==0){ans=ans/i*(i-1);while(n%i==0)n/=i;}}if(n>1)ans=ans/n*(n-1);return ans;
}
关于欧拉函数的性质:
1.任意n>1,1-n中与n互质的数的和为 n* φ(N) /2;
2.若 a,b互质 ,则φ(ab)=φ(a)*φ(b);
积性函数:若a,b互质时,有f(ab)=f(a)*f(b),那么称函数f为积性函数。
3.若f是积性函数,在算术借本定理中,n=∏(i=1-->m)pi^ci,则 f(n)=∏(i=1-->m)f(pi^ci);
4.若p|n且p^2| n ,则φ(n)=φ(n/p)*p;
5.若p|n但 p^2 |/ n, 则φ(n)=φ(n/p)*(p-1)
6.∑d|n φ(d) = n
以上就是欧拉函数的性质,下面看一些例题吧,(毕竟光说不练假把式!)
question1:
可见的点
在一个平面直角坐标系的第一象限内,如果一个点(x,y)与原点(0,0)的连线中没有通过其他任何点,则称该点在原点处是可见的。
例如,点(4,2)就是不可见的,因为它与原点的连线会通过点(2,1)。
部分可见点与原点的连线如下图所示:
编写一个程序,计算给定整数N的情况下,满足0≤x,y≤N0≤x,y≤N的可见点(x,y)的数量(可见点不包括原点)。
输入格式
第一行包含整数C,表示共有C组测试数据。
每组测试数据占一行,包含一个整数N。
输出格式
每组测试数据的输出占据一行。
应包括:测试数据的编号(从1开始),该组测试数据对应的N以及可见点的数量。
同行数据之间用空格隔开。
数据范围
1≤N,C≤10001≤N,C≤1000
输入样例:
4
2
4
5
231
输出样例:
1 2 5
2 4 13
3 5 21
4 231 32549
解题报告:第一眼的思路就是双重循环去求解gcd为1的对数,确实是可以实现的,但是会超时,所以应该使用欧拉函数,很明显可以看出来(1,1) (1,0),(0,1) 这三个点是一定会看到的,那么剩下的就是需要咱们去看的了,这个时候咱们可以只求解y=x一侧的点对数目,因为他们是对称的,当跑到横坐标为x的时候,和这个横坐标gcd等于1的数目不就是x的欧拉函数值吗,所以就可以将双重for降到一重,最后的欧拉函数值累加后,乘2 + 3,就是最后的答案。
ac代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;const int maxn=1100;
int phi[maxn];void eluer()
{for(int i=2;i<maxn;i++){if(!phi[i]){for(int j=i;j<maxn;j+=i){if(!phi[j]) phi[j]=j;phi[j]=phi[j]/i*(i-1);}}}
}int main()
{int t,n;eluer();cin>>t;int kase=0;while(t--){int ans=0;cin>>n;for(int i=2;i<=n;i++)ans+=2*phi[i];ans+=3;cout<<++kase<<" "<<n<<" ";if(ans==0)cout<<"0"<<endl;elsecout<<ans<<endl;} return 0;
}
question2:
GCD
The greatest common divisor GCD(a,b) of two positive integers a and b,sometimes written (a,b),is the largest divisor common to a and b,For example,(1,2)=1,(12,18)=6.
(a,b) can be easily found by the Euclidean algorithm. Now Carp is considering a little more difficult problem:
Given integers N and M, how many integer X satisfies 1<=X<=N and (X,N)>=M.
Input
The first line of input is an integer T(T<=100) representing the number of test cases. The following T lines each contains two numbers N and M (2<=N<=1000000000, 1<=M<=N), representing a test case.
Output
For each test case,output the answer on a single line.
Sample Input
3 1 1 10 2 10000 72
Sample Output
1 6 260
解题报告:看了几个题解,总觉得没有把这道题目的精髓给讲明白,后来自己思考了好久,终于算是有些头绪了,给定n,m,要求解的是在1-n范围内寻找某一个数x,gcd(x,n) >= m. 队友说是欧拉函数,(哭泣,欧拉函数到底在什么时候去使用,还需要去继续研究),然后知道了使用欧拉函数,但是不知道应该去怎么使用啊,后来看了一下直系学长的博客,觉得讲的不错,但是还不够细腻。决定自己写一下:
首先除了n的因子外的数和n的gcd肯定都是1,所以符合题意的x肯定是n的某个因子的倍数,所以咱们去寻找一下n的所有的因子,假设存在一个因子p >= m ,那么咱们就去求解一下 (n/p)的欧拉函数值是多少,累加一下。(至于为什么要求(n/p)的欧拉函数 这是为了解决”冲突“ 对于n的因子p和一个与(n/p)不互素的k k*p就会变成n的其他因子或其他因子的倍数 这里需要仔细想想)
ac代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;ll eluer(ll n)
{ll ans=n;for(ll i=2;i*i<=n;i++){if(n%i==0){ans=ans/i*(i-1);while(n%i==0)n/=i;}}if(n>1)ans=ans/n*(n-1);return ans;
}int main()
{int n,m,t;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);ll ans=0;for(int i=1;i*i<=n;i++){if(n%i==0){if(n/i>=m&&i*i!=n)ans+=eluer(i);if(i>=m)ans+=eluer(n/i);}}printf("%lld\n",ans);}
}
question3:
上帝与集合的正确用法
Description
根据一些书上的记载,上帝的一次失败的创世经历是这样的:
第一天, 上帝创造了一个世界的基本元素,称做“元”。
第二天, 上帝创造了一个新的元素,称作“α”。“α”被定义为“元”构成的集合。容易发现,一共有两种不同的“α”。
第三天, 上帝又创造了一个新的元素,称作“β”。“β”被定义为“α”构成的集合。容易发现,一共有四种不同的“β”。
第四天, 上帝创造了新的元素“γ”,“γ”被定义为“β”的集合。显然,一共会有16种不同的“γ”。
如果按照这样下去,上帝创造的第四种元素将会有65536种,第五种元素将会有2^65536种。这将会是一个天文数字。
然而,上帝并没有预料到元素种类数的增长是如此的迅速。他想要让世界的元素丰富起来,因此,日复一日,年复一年,他重复地创造着新的元素……
然而不久,当上帝创造出最后一种元素“θ”时,他发现这世界的元素实在是太多了,以致于世界的容量不足,无法承受。因此在这一天,上帝毁灭了世界。
至今,上帝仍记得那次失败的创世经历,现在他想问问你,他最后一次创造的元素“θ”一共有多少种?
上帝觉得这个数字可能过于巨大而无法表示出来,因此你只需要回答这个数对p取模后的值即可。
你可以认为上帝从“α”到“θ”一共创造了10^9次元素,或10^18次,或者干脆∞次。
一句话题意:
Input
接下来T行,每行一个正整数p,代表你需要取模的值
Output
T行,每行一个正整数,为答案对p取模后的值
Sample Input
3
2
3
6
Sample Output
0
1
4
解题报告:就是一道看起来很玄乎的题目,因为2的幂次是没有结束的时候,但是咱们可以通过递归和欧拉降幂来将模数变为1,那么所有的数在模后就是0,这就是递归出口,要知道欧拉降幂的原则:
ac代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;ll p;
int t;ll phi(ll x)
{ll res=x;for(int i=2;i*i<=x;i++){if(x%i==0){res=res/i*(i-1);while(x%i==0)x/=i;}}if(x>1)res=res/x*(x-1);return res;
}
ll ksm(ll a,ll b,ll c)
{ll res=1;while(b){if(b&1){res=(res*a)%c;}a=(a*a)%c;b>>=1;}return res;
}
ll slove(ll x)
{if(x==1)return 0;return ksm(2,slove(phi(x))+phi(x),x);
}
int main()
{cin>>t;while(t--){cin>>p;cout<<slove(p)<<endl; }
}
以上就是欧拉函数比较常用的几个应用,以后会慢慢补更多的!
发布评论