素数筛:
1.埃氏筛法(时间复杂度O(nlogn))
const int maxn=1e4+10;
int prime[maxn],pnum=0;//prime数组存放所有素数,pnum为素数个数
bool p[maxn];//如果i为素数,则p[i]为false,否则p[i]为true
void find_prime(){
for(int i=2;i<maxn;i++){
if(p[i]==false){
prime[pnum++]=i;
for(int j=i+i;j<maxn;j+=i){
//筛去i的倍数
p[j]=true;
}
}
}
}
2.欧式筛法(时间复杂度O(n)) 每个合数都由其最小素因子筛去
int prime[maxn],tot;
bool vis[maxn];
void isprime(){
for(int i=2;i<maxn;i++){
if(!vis[i]){
prime[tot++]=i;
}
for(int j=0;j<tot;j++){
if(i*prime[j]>maxn) break;//判断是否越界
vis[i*prime[j]]=1;//筛掉合数
if(i%prime[j]==0) break;//判断因子是否包含素因子
}
}
}
3.快速分解质因数
先预处理质因数,然后枚举质因数,原先的写法是枚举i,这样会有中间不是质因数的循环情况,相当于跳过了不是质因数的情况,在自然数中1-n随机选取一个是质因数的情况大致是1/ln(n),所以这一步相当于优化了log2(n)倍虽然可能只有十几倍但是当数据范围取到2e6的时候nsqrt(n)是会超时的,加上这一步优化就可以过题了
void init(){
for(int i=2;i<=maxn-5;i++){
if(!vis[i]){
prime[++cnt]=i;
}
for(int j=1;j<=cnt;j++){
if(prime[j]*i>maxn){
break;
}
vis[prime[j]*i]=1;
if(i%prime[j]==0){
break;
}
}
}
}
int fenjie(int x,int pos){
int w=0;
for(int i=1;i<=cnt&&prime[i]*prime[i]<=x;i++){
if(x%prime[i]==0){
w++;
ans+=(pos-last[prime[i]]-1)*(n-pos+1);
last[prime[i]]=pos;
while(x%prime[i]==0){
x/=prime[i];
}
}
}
if(x>1){
w++;
ans+=(pos-last[x]-1)*(n-pos+1);
last[x]=pos;
}
return w;
}
欧拉函数:
任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?(比如,在1到8之中,有多少个数与8构成互质关系?) 计算这个值的方法就叫做欧拉函数,以φ(n)表示。在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(n) = 4。
第一种情况 如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。
第二种情况 如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如5与1、2、3、4都构成互质关系。
第三种情况 如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),则
比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。 这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p^(k-1)个,即1×p、2×p、3×p、...、p^(k-1)×p,把它们去除,剩下的就是与n互质的数。 上面的式子还可以写成下面的形式:
可以看出,上面的第二种情况是 k=1 时的特例。
第四种情况 如果n可以分解成两个互质的整数之积,n = p1 × p2 则 φ(n) = φ(p1p2) = φ(p1)φ(p2)
即积的欧拉函数等于各个因子的欧拉函数之积。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24。 这一条的证明要用到"中国剩余定理"
第五种情况
求1-n中与n互质的数的个数 板子
#include<bits/stdc++.h>
using namespace std;
int n,a,res;
int main()
{
cin>>n;
while(n -- )
{
cin>>a;
res = a;
for(int i = 2; i <= sqrt(a); i ++ )//分解质因数
{
if(a % i == 0)
{
res = res / i * (i - 1);//等于 res *= (1 - (1 / i))
while(a % i == 0)//但是1 / i是小数,而res是i的倍数,所以上式一定是整数
a /= i;
}
}
if(a > 1) //到了这一步a自己就是质因数
res = res / a * (a - 1);
cout<<res<<endl;
}
return 0;
}
线性求1-n中各个数的欧拉函数 板子
/*
特性 :
1.若a为质数,phi[a]=a-1;
2.若a为质数,b%a==0,phi[a*b]=phi[b]*a;
3.若a为质数并且a,b互质,phi[a*b]=phi[b]*phi[a]
*/
int main(){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!m[i]){
p[++pump]=i;
phi[i]=i-1; //性质1
}
for(int j=1;j<=pump&&p[j]*i<=n;j++){
m[p[j]*i]=i;
if(i%p[j]==0){
phi[p[j]*i]=phi[i]*p[j]; //性质2
break;
}
else phi[p[j]*i]=phi[i]*phi[p[j]]; //性质3
}
}
}
附:
知识:
① 乘性函数:如果对于任意的正整数n和m,且n和m互素,函数f()有f(n*m)=f(n)*f(m)。
② 如果一个函数f是乘性函数,那么它的和函数F(n)=f(d1)+f(d2)+....+f(dk)也是乘性函数,其中 di | n (即n%di==0)
③ 如果n和m互素,gcd(i,n*m)=gcd(i,n)*gcd(i,m),所有gcd()函数乘性函数。
④ 设phi()是欧拉函数,即phi(n)表示小于n的正整数中与n素数的个数,欧拉函数phi()也是乘性函数。
⑤ 关于欧拉函数有定理:设p是素数,a是一个正整数,则 phi(p^a)=p^a - p^(a-1)。
题意:给一个n,求所有的gcd(i,n)之和,其中1<= i <= n.
trick:设gcd(i,n)==p 即最大公因数为p 则i/p与n/p互质 那么等价于1-n中满足i/p与n/p互质的数的个数=phi(n/p) 再枚举因数p即可求解
#include<cstdio>
#include<cmath>
#define ll long long
ll n,ans,i;
ll euler(int x)
{
int res=x;
for(int i=2; i<=sqrt(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;
}
int main()
{
while(~scanf("%lld",&n))
{
ans=0;
for(i=1; i<sqrt(n); i++)if(n%i==0)
ans+=i*euler(n/i)+n/i*euler(i);
if(i*i==n)ans+=i*euler(i);
printf("%lld\n",ans);
}
}
欧拉降幂:
为了求解这个式子a^bmodc,我们可以怎么做?
暴力pow?快速幂?
很显然,当b大到一定程度时,利用pow或者快速幂这样的算法是无法在给定时间内求解的,这时我们引入欧拉降幂算法,这个算法的特点就是降低幂方的值而不影响最终结果,使我们解决问题的时间缩短。
结论:
给出欧拉降幂的公式:当符合第三种情况即模数与底数不互质且次幂大于等于时可用该公式将次幂降幂
整除分块:
对于一段连续的区间,n/i的值是不变的,那么对于这一段区间,我们就可以跳过,O(1)计算出这一段区间的值 例如:10/6==10/7==10/8==10/9==10/10都等于1,所以可以i从7-10就可以直接跳过,i=10/1+1,i=11跳过 公式:i=(x/(x/i))+1
void init () {
int ans=0;
for(int l=1,r,len;l<=n;l=r+1) {
r=n/(n/l),len=r-l+1;
ans+=len*(n/l);
}
}
求1-n中与x互质的数的个数:
对x进行质因子分解设x的质因子的个数为z,则时间复杂度为O(2^z *z)
vector<ll> pme;
ll count_prime(ll n,ll x){
pme.clear();
for(ll i=2;i<=sqrt(x);++i){
if(x%i==0){
pme.push_back(i);
while(x%i==0) x/=i;
}
}
if(x>1) pme.push_back(x);
ll sum=0;
for(int i=1;i<(1<<pme.size());++i){
ll z=1,num=0;
for(int j=0;j<pme.size();++j)
if(i>>j&1) z*=pme[j],++num;
if(num&1) sum+=n/z;
else sum-=n/z;
}
return n-sum;
}