bzoj4802 欧拉函数(附Millar-Rabin和Pollard-Rho讲解)

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4802

【题解】

参考:http://www.matrix67.com/blog/archives/234

Millar-Rabin质数检验方法:

根据费马小定理,如果p是素数,a<p,那么有a^(p-1) mod p = 1。

直观想法我们直接取若干个a,如果都有一个不满足,那么p就是合数。

遗憾的是,存在Carmichael数:你无论取多少个a,有一个不满足,算我输。

比如:561 = 11*51就是一个Carmichael数。

那么就很江了啊。。我们需要改进算法。

首先有:如果p是素数,x是小于p的正整数,且x^2 mod p = 1,那么要么x=1,要么x=p-1

(这个废话,x=p-1模意义下等于x=-1)

然后我们可以展示下341满足2^340 mod 341 = 1,却不是素数(341=31*11)的原因:

2^340 mod 341 = 1

2^170 mod 341 = 1

2^85 mod 341 = 32

(32这个数很py啊怎么不等于340也不等于1啊。。这明显有交易嘛)

那么就能说明这个数不是素数。

如果是素数,一定是从p-1变到1,或是把所有2的次幂去除完,本来就等于1(这样平方完就一直是1了)

所以要么把所有2的次幂去除完,本来就等于1,要么存在某一个次幂=p-1(这样就正常多了)

这就是Millar-Rabin素数验证的二次探测。

应该来说Millar-Rabin算法也是挺好写的

其中mul(a,b,c)表示a*b%c(因为a*b会爆longlong,所以用快速加)

namespace Millar_Rabin {
    const int Prime[14] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
    const int PN = 13;

    inline bool witness(int pr, ll res, int times, ll n) {
        ll p = pwr2((ll)pr, res, n);
        if(p == 1) return 0;
        for (int i=0; i<times; ++i) {
            if(p == n-1) return false;
            if(p == 1) return false;
            p = mul(p, p, n);
        }
        return true;
    }

    inline bool main(ll n) {
        for (int i=1; i<=PN; ++i) {
            if(n == Prime[i]) return 1;
            if(n % Prime[i] == 0) return 0;
        }
        ll p = n-1;
        int times = 0;
        while(!(p&1)) {
            ++times;
            p >>= 1;
        }
        for (int i=1; i<=PN; ++i)
            if(witness(Prime[i], p, times, n)) return false;
        return true;
    }
}

然后我们会检验素数了,现在要质因数分解。

好了下一个是Pollard-Rho算法:

如果现在拆分的是n:Pollard-Rho(n)

主要流程:Millar-Rabin判断是否质数,是返回,否就试图找出其中一个因子d,然后递归做Pollard-Rho(d)和Pollard-Rho(n/d)。

我猜你会说废话这谁都会。问题在于:试图找出其中一个因子d

参考:https://wenku.baidu.com/view/3db5c7a6ad51f01dc381f156.html?from=search

参考文章讲的非常详细了。。我就不细讲了qwq

所以这题就是分解因数,按照欧拉函数定义式求解即可。

# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

inline ll mul(ll a, ll b, ll mod) {
    ll ret = 0;
    a %= mod, b %= mod;
    while(b) {
        if(b&1) {
            ret = ret + a;
            if(ret >= mod) ret -= mod;
        }
        a <<= 1;
        if(a >= mod) a -= mod;
        b >>= 1;
    }
    return ret;
}

inline ll pwr2(ll a, ll b, ll mod) {
    ll ret = 1;
    a %= mod;
    while(b) {
        if(b&1) ret = mul(ret, a, mod);
        a = mul(a, a, mod);
        b >>= 1;
    }
    return ret;
}

inline ll gcd(ll a, ll b) {
    return b==0 ? a : gcd(b, a%b);
}

namespace Millar_Rabin {
    const int Prime[14] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
    const int PN = 13;

    inline bool witness(int pr, ll res, int times, ll n) {
        ll p = pwr2((ll)pr, res, n);
        if(p == 1) return 0;
        for (int i=0; i<times; ++i) {
            if(p == n-1) return false;
            if(p == 1) return false;
            p = mul(p, p, n);
        }
        return true;
    }

    inline bool main(ll n) {
        for (int i=1; i<=PN; ++i) {
            if(n == Prime[i]) return 1;
            if(n % Prime[i] == 0) return 0;
        }
        ll p = n-1;
        int times = 0;
        while(!(p&1)) {
            ++times;
            p >>= 1;
        }
        for (int i=1; i<=PN; ++i)
            if(witness(Prime[i], p, times, n)) return false;
        return true;
    }
}

namespace PollardRho {
    const int N = 110;
    ll q[N]; int qn;    

    inline void PR(ll n) {
        if(Millar_Rabin::main(n)) {
            q[++qn] = n;
            return ;
        }
        ll a, b, c, del;
        while(1) {
            c = rand() % n;
            a = b = rand() % n;
            b = (mul(b, b, n) + c) % n;
            while(a != b) {
                del = a-b;
                del = gcd(abs(del), n);
                if(del > 1 && del < n) {
                    PR(del); PR(n/del);
                    return ;
                }
                a = (mul(a, a, n) + c) % n;
                b = (mul(b, b, n) + c) % n;
                b = (mul(b, b, n) + c) % n;
            }
        }
    }

    inline ll getphi(ll n) {
        if(n == 1) return 1ll;
        sort(q+1, q+qn+1);
        ll res = q[1] - 1;
        for (int i=2; i<=qn; ++i) {
            if(q[i] != q[i-1]) res = res * (q[i] - 1);
            else res = res * q[i];
        }
        return res;
    }

    inline void main(ll n) {
        qn = 0;
        PR(n);
        cout << getphi(n) << endl;
    }
}

int main() {
    srand(19260817);
    ll n; cin >> n;
    if(n == 1) {
        puts("1");
        return 0;
    }
    PollardRho::main(n);
    return 0;
}

时间: 2024-08-05 15:20:58

bzoj4802 欧拉函数(附Millar-Rabin和Pollard-Rho讲解)的相关文章

数论快速入门(同余、扩展欧几里德、中国剩余定理、大素数测定和整数分解、素数三种筛法、欧拉函数以及各种模板)

数学渣渣愉快的玩了一把数论,来总结一下几种常用的算法入门,不过鶸也是刚刚入门, 所以也只是粗略的记录下原理,贴下模板,以及入门题目(感受下模板怎么用的) (PS:文中蓝色字体都可以点进去查看百度原文) 附赠数论入门训练专题:点我打开专题(题目顺序基本正常,用以配套数论入门) 一.同余定理 简单粗暴的说就是:若 a-b == m 那么 a%m == b%m 这个模运算性质一眼看出...直接上入门水题: Reduced ID Numbers 附AC代码(这个也没啥模板....知道就好) #inclu

BZOJ 4802 欧拉函数

4802: 欧拉函数 Description 已知N,求phi(N) Input 正整数N.N<=10^18 Output 输出phi(N) Sample Input 8 Sample Output 4 很明显,这样的题就是一道十分简单的入门题.只是N比较大,但输入和输出都还没有爆long long.如果输入高精度数,那就很恶心了(虽然可以定义大整数类Big Int). phi[n]=n∏(1-(1/p)),所以是Miller-Rabin和Pollard-Rho算法,用来分解一个大数的质因子.

POJ2480:Longge&#39;s problem(欧拉函数的应用)

题目链接:传送门 题目需求: Given an integer N(1 < N < 2^31),you are to calculate ∑gcd(i, N) 1<=i <=N. 这题就是上一篇博客的变形. 题目解析:首先先求出与N互质的个数,即N的欧拉函数值,之后分解出N的因子来,求解方法如下. 证明: 要求有多少个 i 满足gcd(i, N) = d 如果gcd(i, N) = d,则gcd(i/d, N/d) = 1 由于i <= N,所以 i/d <= N/d,

杭电3501Calculation 2 欧拉函数

Calculation 2 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2982    Accepted Submission(s): 1231 Problem Description Given a positive integer N, your task is to calculate the sum of the posit

POJ 2480 Longge&#39;s problem (欧拉函数+乘性函数)

Longge's problem Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7343   Accepted: 2422 Description Longge is good at mathematics and he likes to think about hard mathematical problems which will be solved by some graceful algorithms. Now

数论讨伐!欧拉函数!

[欧拉函数] 任务开始. 什么是欧拉函数?我们又怎么求呢??? 此次任务的主要怪物:欧拉函数 (1)欧拉函数定义 欧拉函数嘛,当然是我们著名的莱昂哈德·欧拉发明的啦~那么他是怎么定义介个函数滴? 咳咳,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1) 啊?这就完了?好像挺简单的啊... 但是不要小瞧他,看过上一篇讨伐的各位可能还记得这样一张图: 当时是不是对它一知半解有点懵?现在我们来认真的导一下,彻底学习~ (2)欧拉函数性质 1.φ(n)=n-1   当且仅当n为质数

bzoj 4802 欧拉函数 (pollardrho大数质因数分解)

bzoj4802 求\(10^{18}\)级别的数的欧拉函数. pollardrho算法分解大数质因数即可.(主要是存模板) #include <bits/stdc++.h> using namespace std; typedef long long ll; ll sed=20170831,mo=LLONG_MAX,rt=1; ll rand_() { rt=(((rt%sed)<<16)&mo); return rt; } ll range(int l,int r) {

欧拉函数

void Euler_Sieve_Method(int * euler, int n) { euler[1] = 1; for (int i = 2; i < n; i++) { euler[i] = i; } for (int i = 2; i < n; i++) { if (euler[i] == i) { for (int j = i; j < n; j += i) { euler[j] = euler[j] / i * (i - 1); } } } } void Euler_Si

hdu1695(莫比乌斯)或欧拉函数+容斥

题意:求1-b和1-d之内各选一个数组成数对,问最大公约数为k的数对有多少个,数对是有序的.(b,d,k<=100000) 解法1: 这个可以简化成1-b/k 和1-d/k 的互质有序数对的个数.假设b=b/k,d=d/k,b<=d.欧拉函数可以算出1-b与1-b之内的互质对数,然后在b+1到d的数i,求每个i在1-b之间有多少互质的数.解法是容斥,getans函数参数的意义:1-tool中含有rem位置之后的i的质因子的数的个数. 在 for(int j=rem;j<=factor[i