SCUT - 485 - 质因数计数 - 原根

https://scut.online/p/485

给定a和n,求有多少个质数p,满足n是使得a^n=1 mod p成立的最小正整数。

翻译:求有多少个质数p,使得a模p的阶delta_m(a)是n

先验证 a^n=1 mod p 成立

那么假如还有更小的m使得 a^m=1 mod p 成立,则这个p不合要求

由阶的性质有delta_m(a)|n,故只需要检查n的所有因子就可以了。

但其实不需要检查所有因子,只需要检查n的所有质因子。(从板子上面可以看出来,但是为什么)

即 a^(p_i) = 1 mod p 是否成立,假如恒不成立,则n是a模p的阶,其中p_i是n的每种质因子。

证明如下:很显然的,原本我们要检查n的所有因子才能确定阶,但是有一个更好的办法。

假如还有更小的m使得 a^m=1 mod p 成立,那么m的倍数km也一定满足 a^(km)=1 mod p 成立,那么从n中只去除一个质因子p_i,假如这个t=(n/p_i)有 a^t=1 mod p ,则可能存在更小的m使得 a^m=1 mod p 成立。否则假如 a^t != 1 mod p ,则t的所有因子也都不需要检查了。

这样就只需要检查log次。

1e9都直接暴力,快速幂还有个logn,真的服了没话讲。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int q, n;

const int MAXN = 1e7 + 5;

int p[MAXN], ptop;
int minp[MAXN];
bool np[MAXN];

void sieve(int n) {
    np[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!np[i]) {
            p[++ptop] = i;
            minp[i] = i;
        }
        for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
            minp[t] = p[j];
            np[t] = 1;
            if(i % p[j] == 0)
                break;
        }
    }
}

int qpow(ll x, int n, int p) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % p;
        x = x * x % p;
        n >>= 1;
    }
    return res;
}

int ans[MAXN], atop;

int fac[60], ftop;

void GetFactor(int n) {
    ftop = 0;
    while(n != 1) {
        int cp = minp[n];
        fac[++ftop] = cp;
        while(n % cp == 0)
            n /= cp;
    }
}

int check(int n, int P) {
    if(qpow(q, n, P) != 1)
        return 0;
    for(int i = 1; i <= ftop; ++i) {
        int t = n / fac[i];
        if(qpow(q, t, P) == 1)
            return 0;
    }
    return 1;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    sieve(MAXN);
    while(~scanf("%d%d", &q, &n)) {
        GetFactor(n);
        atop = 0;
        for(int i = 1; i <= ptop; ++i) {
            if(check(n, p[i]))
                ans[++atop] = p[i];
        }
        printf("%d\n", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d%c", ans[i], " \n"[i == atop]);
    }
    return 0;
}

直接上BSGS的话,假如这样的p有10000个就比较麻烦。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int q, n;

const int MAXN = 1e7 + 5;

int p[MAXN], ptop;
bool np[MAXN];

void sieve(int n) {
    np[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!np[i])
            p[++ptop] = i;
        for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
            np[t] = 1;
            if(i % p[j] == 0)
                break;
        }
    }
}

int qpow(ll x, int n, int p) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % p;
        x = x * x % p;
        n >>= 1;
    }
    return res;
}

unordered_map<int,int> M;
int bsgs(int a,int b,int n,int k=1,int t=0){
    /*if(b==1)
        return 0;*/
    M.clear();
    int m=ceil(sqrt(n));
    ll s=b;
    for(int i=0;i<m;++i,s=s*a%n){
        M[s]=i;
    }
    s=k;
    k=qpow(a,m,n);
    for(ll i=1;i<=m;++i){
        s=s*k%n;
        if(M.count(s))
            return i*m-M[s]+t;
    }
    return -1;
}


int ans[MAXN], atop;

int check(int n, int p) {
    if(qpow(q, n, p) != 1)
        return 0;
    int res=bsgs(q,1,p);
    if(res==-1||res==n)
        return 1;
    return 0;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    sieve(MAXN);
    while(~scanf("%d%d", &q, &n)) {
        atop = 0;
        for(int i = 1; i <= ptop; ++i) {
            if(check(n, p[i]))
                ans[++atop] = p[i];
        }
        printf("%d\n", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d%c", ans[i], " \n"[i == atop]);
    }
    return 0;
}
posted @ 2019-08-11 22:32  韵意  阅读(281)  评论(0编辑  收藏  举报