BZOJ4802: 欧拉函数 pollard_rho

题意:给出n,求phi(n)
温习一下太久没用过的板子。
miller_rabin:基于费马小定理a^p-1==1 mod p
和二次探测定理 a^2==1 mod p <==> a==1 or a==p-1 mod p
单次测试的正确率约为75%。
pollard_pho:设置两个初值相同的变量,每次平方后加上一个常数,一个变量一次走一步,另一个一次走两步,若gcd(p,abs(a-b))!=1则找到一个因子,然后递归两边。如果出现环了还没找到则更换初值和常数继续找。
时间复杂度四次根号p。
注意miller_rabin和pollard_pho里的各种特判。
n==1需要特判掉。

#include<cstdio>
#include<tr1/random>
#include<ext/pb_ds/assoc_container.hpp>
#ifdef ONLINE_JUDGE
#define null_type __gnu_pbds::null_mapped_type
#else
#define null_type __gnu_pbds::null_type
#endif
using namespace std;
typedef long long ll;
inline ll mul(const ll &a,const ll &b,const ll &m)
{
    return ((a*b-(ll)((long double)a/m*b)*m)+m)%m;
}
inline ll qpow(ll a,ll x,const ll &m)
{
    ll ans=1;
    while(x)
    {
        if(x&1) ans=mul(ans,a,m);
        a=mul(a,a,m);
        x>>=1;
    }
    return ans;
}
inline bool miller_rabin(ll a,ll u,int c,ll p)
{
    a=qpow(a,u,p);
    while(c--)
    {
        ll b=mul(a,a,p);
        if(b==1&&(a!=1&&a!=p-1)) return 0;
        a=b;
    }
    return a==1;
}
inline bool is_prime(const ll &p)
{
    if(p==2||p==3||p==5) return 1;
    if(p<2||!(p&1)) return 0;
    ll u=p-1;
    int c=0;
    while(!(u&1)) u>>=1,++c;
    return miller_rabin(2,u,c,p)&&miller_rabin(3,u,c,p)&&miller_rabin(5,u,c,p);
}
inline ll gcd(ll a,ll b)
{
    ll c;
    while(b)
    c=a,a=b,b=c%b;
    return a;
}
inline ll pollard_pho(ll a,ll c,ll p)
{
    if(!(p&1)) return 2;
    ll b=(mul(a,a,p)+c)%p;
    ll res=gcd(p,a<b?b-a:a-b);
    while(res==1)
    {
        a=(mul(a,a,p)+c)%p;
        b=(mul(b,b,p)+c)%p,b=(mul(b,b,p)+c)%p;
        res=gcd(p,a<b?b-a:a-b);
    }
    return res;
}
__gnu_pbds::cc_hash_table<ll,null_type> t;
tr1::mersenne_twister<unsigned long long,64,312,156,31,0xb5026f5aa96619e9ULL,29,17,0x71d67fffeda60000ULL,37,0xfff7eee000000000ULL,43> en(114514);
ll stk[50];
int tot=0;
void decompose(ll x)
{
    if(t.find(x)!=t.end()) return;
    t.insert(x);
    if(is_prime(x))
    {
        stk[++tot]=x;
        return ;
    }
    ll y=x;
    tr1::uniform_int<long long> rnd(1,x-1);
    while(x==y)
    y=pollard_pho(rnd(en),1,x);
    decompose(y);decompose(x/y);
}
ll n;
int main()
{
    scanf("%lld",&n);
    if(n==1)
    {
        puts("1");
        return 0;
    }
    decompose(n);
    for(int i=1;i<=tot;++i)
    n-=n/stk[i];
    printf("%lld\n",n);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值