解一类x^a = b (mod p)的方程

Problem 1. modlog
Input file: modlog.in
Output file: modlog.out
Time limit: 1 second
Memory limit: 256 MB
Mr.H 最近在练习解方程,最近他发现了一类有趣的方程,希望你能帮忙解决。给出a; b; p,希望你能
帮忙解如下方程:x^a = b (mod p)
其中:0 <= x < p (保证p 是素数)
你需要输出解的个数,你还可能需要输出这些解的和。
Input
第1 行1 个整数:T,表示方程个数。
接下来1 行,一个整数type,如果type = 0,表示不需要输出解的和,如果type = 1,表示需要输出解的和。
接下来T 行,每行3 个整数:a b p,表示一个方程。
Output
对于每个方程,输出1 行,第一个整数表示解的个数,如果type = 1, 还需要在同一行输出所有解的和摸p。
Sample
modlog.in
2
1
1 3 5
2 4 7
modlog.out
1 3
2 0
modlog.in
2
0
1 3 5
2 4 7
modlog.out
1
2
Note
. 对于10% 的数据,2 <= p <= 50;
. 对于30% 的数据,2 <= p <= 1000;
. 对于另外30% 的数据,2 <= p <= 10^9 + 10,type = 0。
. 对于100% 的数据,2 <= p <= 10^9 + 10,0 <= type <= 1,1 <= a <= p .. 1, 0 <= b < p, 1 <= T <= 1000。

思路:
1.1 10%
暴力,不用快速幂。
1.2 30%
暴力,用快速幂。
1.3 另外30%
xa = b (mod m)
如果b 为0,那么只有x = 0 一个解。所以以下讨论都建立在b != 1 的基础上。
首先找到m 的一个元根g,然后求b 的离散对数indgb,方程就变成了:
aindgx = indgb (mod m-1)
这个方程的每个解对应于原方程的一个解。
具体来说,如果gcd(a,m-1) 不整除 indgb,那么原方程无解,否则有gcd(a,m-1) 个解。
1.4 100%
假如我们解出了上面那个方程:
indgx = r (mod s)
其中:
s = (m-1)/gcd(a,m-1)
我们的gcd(a,m-1) 个解就是:
g^r; g^r+s; g^r+2s; ….; g^r+m-1-s
如果公比是1,即g^s = 1 (mod m),那么和就是gcd(a,m-1)g^r。
如果公比不是1,那么由等比数列求和公式我们会发现答案为0 (mod m)。

原根的求解证明
解一类A^x=B(mod C)(C是质数)的方程 (BSGS)

#include 
#include 
#include 
#include 
using namespace std;

const int lim=100005;
const int S=100007;
bool isnot[lim];
int prim[lim],top,num[lim],ptot=0;

struct Hash_{
    int head[S], dest[S][2], last[S], etot;
    void init(){
        memset(head, 0, sizeof(head));
        etot = 0;
    }
    void add(int a, int b){
        int key = a % S;
        for(int t=head[key]; t; t=last[t])
            if(dest[t][0] == a) return;
        etot++;
        last[etot] = head[key];
        dest[etot][0] = a;
        dest[etot][1] = b;
        head[key] = etot;
    }
    int find(int a){
        int key = a % S;
        for(int t=head[key]; t; t=last[t])
            if(dest[t][0] == a) return dest[t][1];
        return -1;
    }
}hs;

void init(){ 
    for(register int i=2; i<=lim; i++){
        if(!isnot[i]) prim[++ptot] = i;//
        for(register int j=1; j<=ptot&&i*prim[j]<=lim; j++) {
            isnot[prim[j] * i] = true;
            if(!(i % prim[j])) break;
        }
    }
}  

int mpow(int a, int b, int mod) {
    int res = 1;
    while( b ){
       if(b & 1) res = (1LL * res * a) % mod;
       a = (1LL * a * a) % mod;
       b = b >> 1;
    }
    return res;
}

void exgcd(int a, int b, int &d, int &x, int &y) {
    if(b == 0){
        d = a; x = 1; y = 0;
    } 
    else{
        exgcd(b, a%b, d, y, x);
        y -= a / b * x;
    }
}

int gcd( int aa, int bb ) {
    if(bb == 0) return aa;
    int d = gcd( bb, aa % bb );
    return d;
}

int inverse(int a, int m){//逆元 
    int x, y, d;
    exgcd(a, m, d, x, y);
    return (x % m + m) % m;
}

int BSGS(int g, int b, int m){//g^x = a(mod m)
    hs.init();
    int sz = (int)ceil( sqrt(b) + 1 );
    int cur = 1;
    for(register int i=0; i1LL*cur*g)%m){
        if(cur == b) return i;
        hs.add(cur, i);
    }
    int base = inverse(cur, m);
    cur = 1LL * base * b % m;
    for(register int i=sz; i<=m-1; i+=sz,cur=(1LL*cur*base)%m) {
        int j = hs.find(cur);
        if(j != -1) return i + j;
    }
    return -1;
}

void divi(int n){//分解质因数 
    top = 0;
    for(register int i=1; prim[i]*prim[i]<=n; i++)
     if(n % prim[i] == 0){
        num[++top] = prim[i];
        while(n % prim[i] == 0) n /= prim[i];
     }
    if(n != 1) num[++top] = n;//
}

int root(int p){//求原根 (阶=phi(mod))
    divi(p - 1);
    for(int g=1; ; g++){//枚举原根 
        bool flag = true;
        for(int i=1; i<=top; i++){
            int goal = (p-1) / num[i];
            if(mpow(g, goal, p) == 1) {//check
                flag = false; break;
            }
        }
        if(flag) return g;
    }
    return -1;
}

int main(){
    freopen("modlog.in","r",stdin);
    freopen("modlog.out","w",stdout);
    int cnt=0, sum=0, opt, T, a, b, p;
    init(); cin >> T >> opt;
    while( T-- ){
       scanf("%d%d%d", &a, &b, &p);
       if( !b ){
          if(!opt) {puts("1"); continue;}
          else {puts("1 0"); continue;}
       }
       int g = root(p);
       int indb = BSGS(g, b, p), mod = p - 1, phi = mod;
       int d = gcd(a, phi); //a*ind(x) = ind(b) mod(phi(p))
       if(indb % d != 0){
          if(!opt) {puts("0"); continue;}
          else {puts("0 0"); continue;}
       }
       int aa=a, bb=indb;
       aa/=d, bb/=d, mod/=d;
       bb = 1LL * bb * inverse(aa, mod) % mod;
       cnt = phi / mod;
       int q = mpow(g, mod, p);
       if(q == 1) sum = 1LL * d * mpow(g, bb, p) % p;
       else sum = 0;
       if(!opt) printf("%d\n", cnt);
       else printf("%d %d\n", cnt, sum);
    }
    return 0;
}

你可能感兴趣的:(—————数论—————,BSGS,—————模板—————)