最近整理的一些板子

埃氏筛法

用途:找出1-n之间的所有素数

复杂度 o( n l o g l o g n nloglogn nloglogn)

思想:用素数筛掉他的倍数(比如用2筛掉4,6,8,10…,用3筛掉6,9,12,15,18…)

缺点:筛的时候会有重复,比如6会被2和3筛两次

代码

const int maxn=1e7;
int prime[maxn];
int vis[maxn];
int tot=0;
void aishi(){
    for(int i=2;i<=maxn;i++){
        if(!vis[i])  
        {
            prime[++tot]=i; 
            for(int j=1;i*j<=maxn;j++)
            {
                vis[i*j]=1;
            }
        }
    }
}

欧拉筛法

用途:找出1-n之间的所有素数

复杂度o(n)

思想:用素数去筛所有最小因子是这个素数的数

const int maxn=1e7;
int prime[maxn];
int vis[maxn];
int tot=0;
void oula(){
    for(int i=2;i<=maxn;i++){
        if(!vis[i]) prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<=maxn;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) break;//关键
        }
    }
}

区间筛法

用途:找出l-r之间所有的素数

思想:欧拉筛找出 r \sqrt{r} r 以内的素数,再用埃氏筛去筛l-r之间的素数

const long long maxn = 60000+10; //sqtr(r)的最大值
const long long len = 2e6 + 10;    //区间最大长度
long long prime1[maxn];          //用来筛区间的素数
long long vis1[maxn];            //同上
long long tot1 = 0;
void oula()
{
    for (long long i = 2; i <= maxn; i++)
    {
        if (!vis1[i])
            prime1[++tot1] = i;
        for (long long j = 1; j <= tot1 && i * prime1[j] <= maxn; j++)
        {
            vis1[prime1[j] * i] = 1;
            if (i % prime1[j] == 0)
                break;
        }
    }
}
long long prime2[len]; //区间里面的素数
long long vis2[len];   //下标减去了l
long long tot2 = 0;    //区间素数个数
void qujian(long long l, long long r)
{
    memset(vis2, 0, sizeof(vis2));
    tot2 = 0;
    for (long long i = 1; i <= tot1&&prime1[i]*prime1[i]<=r; i++)
    {
        for (long long j = l / prime1[i]; j <= r / prime1[i] ; j++)
        {
            if (j * prime1[i] >= l && j * prime1[i] <= r)
            {
                if (j == 1)
                    continue;
                vis2[j * prime1[i] - l] = 1;
            }
        }
    }
    for (long long i = 0; i <= r - l; i++)
    {
        if (vis2[i] == 0&&i+l!=1)
        {
            prime2[++tot2] = i + l;
        }
    }
}

Miller_Rabin

用途:判断素数

复杂度o( t l o g n tlog{n} tlogn)//t是检验次数

思想:利用飞马小定理和二次探测定理判断

ll multi(ll a, ll b, ll mod)
{
    ll ans = 0;
    while (b)
    {
        if (b & 1)
        {
            ans = (ans + a) % mod;
        }
        b >>= 1;
        a = (a + a) % mod;
    }
    return ans;
}

ll qpow(ll a, ll b, ll mod)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)
        {
            ans=multi(ans,a,mod);
        }
        b >>= 1;
        a=multi(a,a,mod);
    }
    return ans;
}

bool Miller_Rabin(ll n){
    if(n==2) return true;
    if(n<2||!(n&1)) return false;
    ll k=0;
    ll m=n-1;
    while(!(m&1)){
        k++;
        m>>=1;
    }
    int times=10;
    for(int i=0;i<times;i++){
        ll a=rand()%(n-1)+1;
        ll x=qpow(a,m,n);
        ll y=0;
        for(int j=0;j<k;j++){
            y=multi(x,x,n);
            if(y==1&&x!=1&&x!=n-1) return false;
            x=y;
        }
        if(y!=1) return false;
    }
    return true;
}

Pollard_rho

用途:大整数分解

复杂度: o ( n 1 / 4 ) o(n^{1/4}) o(n1/4)

思想:还不太明白以后补上

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
ll gcd(ll a,ll b){
    return (!b)?a:gcd(b,a%b);
}
inline ll multi(ll a, ll b, ll mod)
{
    ll c=(ld)a/mod*b;
    ll ans=(ull)a*b-(ull)c*mod;
    return ans;
}

inline ll qpow(ll a, ll b, ll mod)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)
        {
            ans=multi(ans,a,mod);
        }
        b >>= 1;
        a=multi(a,a,mod);
    }
    return ans;
}

bool Miller_Rabin(ll n){
    if(n==2) return true;
    if(n<2||!(n&1)) return false;
    ll k=0;
    ll m=n-1;
    while(!(m&1)){
        k++;
        m>>=1;
    }
    int times=10;
    for(int i=0;i<times;i++){
        ll a=rand()%(n-1)+1;
        ll x=qpow(a,m,n);
        ll y=0;
        for(int j=0;j<k;j++){
            y=multi(x,x,n);
            if(y==1&&x!=1&&x!=n-1) return false;
            x=y;
        }
        if(y!=1) return false;
    }
    return true;
}

ll pollard_rho(ll n,ll c){
    ll i=1,k=2;
    ll x=rand()%(n-1)+1;
    ll y=x;
    while(1){
        i++;
        x=(multi(x,x,n)+c)%n;
        ll d=gcd(abs(y-x),n);
        if(d>1&&d<n) return d;
        if(x==y) return n;
        if(i==k){
            y=x;
            k<<=1;
        }
    }
}

ll fac[1000];
ll tot=0;

void findfac(ll n,ll c){
    if(n==1) return ;
    if(Miller_Rabin(n)){
        fac[++tot]=n;
        return ;
    }
    ll p=n;
    ll k=c;
    while(p>=n) p=pollard_rho(p,c--);
    findfac(p,k);
    findfac(n/p,k);
}

int main(){
    ll n;
    cin>>n;
    findfac(n,rand()%(n-1)+1);
    for(int i=1;i<=tot;i++) cout<<fac[i]<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值