数论

2 篇文章 0 订阅

素数密度:

对正整数 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值