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