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;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM