素数密度:
对正整数 x,记 π(x) 为不大于 x 的素数个数。则:
从<=n的自然数中选出一个数为素数的概率为 1/lnn
第 n个素数 p(n) 的渐进估计为,p(n)∼nlnn
判断一个大数是否为质数
int isPrime(ll n)
{ //返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
double n_sqrt;
if (n == 2 || n == 3) return 1;
if (n % 6 != 1 && n % 6 != 5) return 0;
n_sqrt = floor(sqrt((float)n));
for (int i = 5; i <= n_sqrt; i += 6)
{
if (n % (i) == 0 | n % (i + 2) == 0) return 0;
}
return 1;
}
或:Miller_Rabin
时间复杂度:O(NUM*logn),k为测试轮数
bool check(LL a,LL n,LL r,LL s)
{
LL ans,p,i;
ans=pow_mod(a,r,n);
p=ans;
for(i=1;i<=s;i++)
{
ans=mul_mod(ans,ans,n);
if(ans==1&&p!=1&&p!=n-1)return true;
p=ans;
}
if(ans!=1)return true;
return false;
}
bool Miller_Rabin(LL n)//Miller_Rabin算法,判断n是否为素数
{
if(n<2)return false;
if(n==2)return true;
if(!(n&1))return false;
LL i,r,s,a;
r=n-1;s=0;
while(!(r&1)){r=r>>1;s++;}
for(i=0;i<NUM;i++)//NUM为运算次数,10等
{
a=rand()%(n-1)+1;
if(check(a,n,r,s))
return false;
}
return true;
}
线性筛质数
int const SIZE=100;
bool Iscomp[SIZE]={false};
int p[SIZE];
int pcnt=0;
void sieve2()
{
for(int i=2;i<SIZE;i++)
{
if(!Iscomp[i])p[pcnt++]=i;
for(int j=0;j<pcnt&&i*p[j]<SIZE;j++)
{
Iscomp[p[j]*i]=true;
if(0==i%p[j])break;
}
}
}
Pollard_rho与Miller_Rabin分解质因数
//o(n^(1/4))
typedef __int64 LL;
const LL NUM=10;//运算次数,Miller_Rabin算法为概率运算,误判率为2^(-NUM);
int cnt;//cnt为质因数个数(注意并不是不同的,如4的话cnt=2,f为2,2)
LL f[100];//f保存质因数,(注意并不是不同的,如4的话cnt=2,f为2,2,不按大小顺序)
LL mul_mod(LL a,LL b,LL n)//求a*b%n,由于a和b太大,需要用进位乘法
{
a=a%n;
b=b%n;
LL s=0;
while(b)
{
if(b&1)
s=(s+a)%n;
a=(a<<1)%n;
b=b>>1;
}
return s;
}
LL pow_mod(LL a,LL b,LL n)//求a^b%n
{
a=a%n;
LL s=1;
while(b)
{
if(b&1)
s=mul_mod(s,a,n);
a=mul_mod(a,a,n);
b=b>>1;
}
return s;
}
bool check(LL a,LL n,LL r,LL s)
{
LL ans,p,i;
ans=pow_mod(a,r,n);
p=ans;
for(i=1;i<=s;i++)
{
ans=mul_mod(ans,ans,n);
if(ans==1&&p!=1&&p!=n-1)return true;
p=ans;
}
if(ans!=1)return true;
return false;
}
bool Miller_Rabin(LL n)//Miller_Rabin算法,判断n是否为素数
{
if(n<2)return false;
if(n==2)return true;
if(!(n&1))return false;
LL i,r,s,a;
r=n-1;s=0;
while(!(r&1)){r=r>>1;s++;}
for(i=0;i<NUM;i++)
{
a=rand()%(n-1)+1;
if(check(a,n,r,s))
return false;
}
return true;
}
LL gcd(LL a,LL b)
{
return b==0?a:gcd(b,a%b);
}
LL Pollard_rho(LL n,LL c)//Pollard_rho算法,找出n的因子
{
LL i=1,j,k=2,x,y,d,p;
x=rand()%n;
y=x;
while(true)
{
i++;
x=(mul_mod(x,x,n)+c)%n;
if(y==x)return n;
if(y>x)p=y-x;
else p=x-y;
d=gcd(p,n);
if(d!=1&&d!=n)return d;
if(i==k)
{
y=x;
k+=k;
}
}
}
void Find(LL n)//找出n的所有因子,每次调用该函数即可,记得cnt初始化为0
{
if(Miller_Rabin(n))
{
f[cnt++]=n;//保存所有因子
return;
}
LL p=n;
while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);//由于p必定为合数,所以通过多次求解必定能求得答案
Find(p);
Find(n/p);
}
int main()
{
srand(time(NULL));
cnt=0;
//特判n==1!
}
求 n! 下质因子p的幂
n! 下 p 的幂 = [ n/p ] + [ n/(p^2) ] + [ n/(p^3) ] ...
C. Trailing Loves (or L'oeufs?)
theme:求n! 转化为b进制后后缀0的个数。n<=1e18,b<=1e12
solution:由十进制转化为b进制操作为依次将n%b的值作为后位,再将n/b,所以此题转化为求n!能分解成b的多少次幂。所以先将b分解质因数,再对每一个b的质因数求解n!能分解的次数,取最小值即为答案。
theme:求n! 转化为b进制后后缀0的个数。即求n!能分解成b的多少次幂
#include <bits/stdc++.h>
using namespace std;
long long n, b;//n<=1e18,b<=1e12
void Solve()//求n!能分解成b的多少次幂(n,b为任意值)
{
long long ans = 1000000000000000000LL;
for (long long i=2; i<=b; i++)
{
if (1LL * i * i > b) i = b;//避免b为质数则cnt=0,所以不是筛到sqrt(i),而是要到i
int cnt = 0;//分解质因子后i的指数应为cnt
while (b % i == 0)
{
b /= i;
cnt++;
}
if (cnt == 0) continue;
long long tmp = 0, mul = 1;//mul表示i^k次方
while (mul <= n / i)
{
mul *= i;
tmp += n / mul;
}
ans = min(ans, tmp / cnt);
}
cout << ans << "\n";
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(NULL);
cin >> n >> b;
Solve();
return 0;
}
试除法分解质因数O(sqrt(n))
const int N=1e6+5;
long long P[N],cnt=0;
bool isprime[N];
void prime()
{
cnt=0;
memset(isprime,1,sizeof(isprime));//初始化认为所有数都为素数
isprime[0]=isprime[1]=0;//0和1不是素数
for(long long i=2; i<N; i++)
{
if(isprime[i])
P[cnt++]=i;//保存素数i
for(long long j=1; j<cnt&&P[j]*i<N; j++)
{
isprime[P[j]*i]=0;//筛掉小于等于i的素数和i的积构成的合数
}
}
}
void solve(int x)
{
for(int i=0; P[i]*P[i]<=x; i++)
{
if(x%P[i]==0)
{
ans.push_back(P[i]);
while(x%P[i]==0)
x/=P[i];
}
}
if(x>1)
ans.push_back(x);
}
组合数取模
1、当n,m较小时,利用C(n,m)=C(n-1,m-1)+C(n-1,m)直接计算
2、当n,m很大,且p为素数时,利用lucas定理
Lucas定理:求组合数C(n,m)%p,p为素数
C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
也就是Lucas(n,m)%p=Lucas(n/p,m/p)*C(n%p,m%p)%p
a、p较小(<=1e5),打表预处理出1-p内所有阶乘%p的值。
const int maxn = 1e5 + 10;
ll fac[maxn];//阶乘打表
void init(ll p)//此处的p应该小于1e5,这样Lucas定理才适用
{
fac[0] = 1;
for(int i = 1; i <= p; i++)
fac[i] = fac[i - 1] * i % p;
}
ll pow(ll a, ll b, ll m)
{
ll ans = 1;
a %= m;
while(b)
{
if(b & 1)ans = (ans % m) * (a % m) % m;
b /= 2;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
return pow(x, p - 2, p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
if(m > n)return 0;
return fac[n] * inv(fac[m] * fac[n - m], p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
if(m == 0)return 1;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}
b、p较大时
//n,m较大,p也较大时,逐项求出分母和分子模上p的值
ll pow(ll a, ll b, ll m)//快速幂
{
ll ans = 1;
a %= m;
while(b)
{
if(b & 1)ans = (ans % m) * (a % m) % m;
b /= 2;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
return pow(x, p - 2, p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
if(m > n)return 0;
ll up = 1, down = 1;//分子分母;
for(int i = n - m + 1; i <= n; i++)up = up * i % p;
for(int i = 1; i <= m; i++)down = down * i % p;
return up * inv(down, p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
if(m == 0)return 1;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}