先确认一下入门数论的基本知识点:
最大公约数
快速幂
素数筛
辗转相除求最大公约数:
int gcd(int a,int b)
{
if(b==0) return a;
gcd(b,a%b);
}
素数筛的模板(线性筛):将2到n之间的整数记录下来,其中最小的素数是2,将表中2的倍数全部不划去,表中剩余的最小的数字为3,再将3的倍数全划去,以此类推,就能找到所有的素数。这样做的时间复杂度只有o(nlog log n)。
int prime[maxn];///用于标记晒是否已经筛过
int pri[maxn];///用于储存素数
void pre_prime()
{
memset(prime,0,sizeof(prime));
cnt = 0;
prime[0] = prime[1] = 1;
for(int i = 2 ; i < maxn ; i++)
{
if(!prime[i]) pri[cnt++] = i;
for(int j = 0 ; j < cnt && i * pri[j] <= maxn ; j++)
{
prime[i * pri[j]] = 1;
if(i % pri[j] == 0) break;///已经筛过的不要重复筛
}
}
}
快速幂:数论问题经常遇见非常大的数,例如 X ^ 22,通常的思路是采用for循环,但当数很大时运算效率会很低。
采用快速幂算法可以将X22拆为X16 , X4和X2。时间复杂度直降至o(log n)。
typedef long long ll;
ll quick_pow(ll x,ll n,ll mod)
{
ll ans=1;
while(n>0)
{
if(n&1)
ans=ans*x%mod;
x=x*x%mod;
n>>=1;
}
return ans;
}
敲黑板:n&1的作用是判断最后一位是否是1,是一的话就是要单乘一次再翻倍(因为有右移n>>1),不是一的话直接翻倍.
欧拉函数
欧拉函数是少于或等于n的数中与n互质的数的数目。
欧拉函数的性质:它在整数n上的值等于对n进行素因子分解后,所有的素数幂上的欧拉函数之积。
欧拉函数的值 通式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)……(1-1/pn),其中p1, p2……pn为x的所有质因数,x是不为0的整数。φ(1)=1(唯一和1互质的数(小于等 于1)就是1本身)。 (注意:每种质因数只一个。比如12=223,那φ(12)=12*(1-1/2)*(1-1/3)=4)
推论:当n为奇数时,有φ(2n)=φ(n)。
若n是质数p的k次幂,φ(n)=pk-p(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质。
设n为正整数,以 φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值,这里函数φ:N→N,n→φ(n)称为欧拉函数。
欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)。
特殊性质:当n为奇数时,φ(2n)=φ(n), 证明与上述类似。
那么如果我们想要求出其欧拉函数的值就要先对n进行质因子分解:
如果进行直接进行筛因子的话其时间复杂度为O(n),代码如下
int prime(int n)
{
int rea=n;
for(int i=2; i<=n; i++)
if(n%i==0)//第一次找到的必为素因子
{
rea=rea-rea/i;
do
n/=i;//把该素因子全部约掉
while(n%i==0);//比如12=2*2*3这样可以将所有的2除去
}
return rea;
}
有没有方法将其降低复杂度呢?我们知道 “ 由于任何一个合数都至少有一个不大于根号n的素因子 ”,所以只需遍历到根号n即可
int prime(int n)
{
int rea=n;
for(int i=2; i*i<=n; i++)
if(n%i==0)//第一次找到的必为素因子
{
rea=rea-rea/i;
do
n/=i;//把该素因子全部约掉
while(n%i==0);
}
if(n>1)
rea=rea-rea/n;//如果到最后n有就将n直接处理
return rea;
}
最后学长说用筛法求欧拉函数很好(可能我太菜了吧,,,,没有领会到)
primel prime[50000];
int pri[20000];
void prim()
{
memset(prime,0,sizeof(prime));
prime[0]=prime[1]=1;
int k=0;
for(int i=2; i<50000; i++)
{
if(!prime[i])
pri [k++]=i;
for(int j=0; j1)
rea=rea-rea/n;
return rea;
}