1. 程式人生 > >poj 2417 小步大步演算法+費馬小定理求逆元

poj 2417 小步大步演算法+費馬小定理求逆元

/*小步大步演算法+費馬小定理求逆元
如果p為素數,a為整數,則a^(p-1)=1(mod p) -> a^(p-2)=(a^(-1))(mod p),又想了下,這個條件成立要a<p;那麼一開始取模就行了。

Baby-Steps-Giant-Steps演算法 
高次同餘方程。   A^x== B (mod C)
這裡用到baby_step,giant_step演算法。意為先小步,後大步。
令x=i*m+j  (m=ceil(sqrt(p))),
那麼原式化為 A^(i*m)*A^j==B(MOD C) ————> A^j===B*A^(-i*m)(MOD C)
我們先預處理A^0,A^1,A^2……A^(m-1),存入HASH表。,這一步就是baby-step,每次移動1
然後求出A^-m,列舉i,如果存在A^(-i*m)存在於HASH表中,說明存在解x=i*m+j    ,這一步為giant_step,每次移動m
至於B^(-m)的求法,可以先求出B的逆元,也就是B^-1。
注意以上解法是最基本的,只能對於C為素數 
*/
//資料比較大,凡是有相乘的地方都要注意溢位 
//Accepted	4608K	4032MS	G++	1365B 莫名通過 
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<set>
#define ll long long
using namespace std;
ll C,A,B;
ll fast_pow(ll a,ll b){
	//求解(a^b)%C的值
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%C;
		b>>=1;
		a=(a*a)%C;
	}
	return ans;
}
int main(){
	while(~scanf("%lld%lld%lld",&C,&A,&B)){
		int m=(int)ceil(sqrt(C));
		map<int,int> hash;
		set<int> vis;
		map<int,int>::iterator it,end=hash.end();
		for(int i=0;i<m;i++)
		{
			//此操作時間複雜度到了m*logm,打表 
			int p=fast_pow(A,i);
			//it=hash.find(p);
			if(vis.count(p)==0){
				vis.insert(p);
				hash[p]=i;
			}
		}
		int t=fast_pow(fast_pow(A,C-2),m);//先求逆元A^(-1),再求A^(-i*m)
		ll tt=B;
		ll ans=-1;
		for(int i=0;i<m;i++)//列舉i 
		{
			//
			if(vis.count(tt))
			{
				//it=hash.find(tt);
				ans=m*i+hash[tt];
				break;
			}
			tt=(tt*t)%C;
		}
		if(ans==-1)
		puts("no solution");
		else
		printf("%lld\n",ans);
	}
	return 0;
}
//莫名加速 Accepted	5628K	2454MS	G++ 
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<set>
#define ll long long
using namespace std;
ll C,A,B;
ll fast_pow(ll a,ll b){
	//求解(a^b)%C的值
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%C;
		b>>=1;
		a=(a*a)%C;
	}
	return ans;
}
int main(){
	while(~scanf("%lld%lld%lld",&C,&A,&B)){
		int m=(int)ceil(sqrt(C));
		map<int,int> hash;
		map<int,bool> vis;
		map<int,int>::iterator it,end=hash.end();
		for(int i=0;i<m;i++)
		{
			//此操作時間複雜度到了m*logm,打表 
			int p=fast_pow(A,i);
			//it=hash.find(p);
			if(vis[p]==0){
				vis[p]=1;
				hash[p]=i;
			}
		}
		int t=fast_pow(fast_pow(A,C-2),m);//先求逆元A^(-1),再求A^(-i*m)
		ll tt=B;
		ll ans=-1;
		for(int i=0;i<m;i++)//列舉i 
		{
			if(vis[tt])
			{
				//it=hash.find(tt);
				ans=m*i+hash[tt];
				break;
			}
			tt=(tt*t)%C;
		}
		if(ans==-1)
		puts("no solution");
		else
		printf("%lld\n",ans);
	}
	return 0;
}



//莫名超時 
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#define ll long long
using namespace std;
ll C,A,B;
ll fast_pow(ll a,ll b){
	//求解(a^b)%C的值
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%C;
		b>>=1;
		a=(a*a)%C;
	}
	return ans;
}
int main(){
	while(~scanf("%lld%lld%lld",&C,&A,&B)){
		int m=(int)ceil(sqrt(C));
		map<int,int> hash;
		map<int,int>::iterator it,end=hash.end();
		for(int i=0;i<m;i++)
		{
			//此操作時間複雜度到了m*logm,打表 
			int p=fast_pow(A,i);
			it=hash.find(p);
			if(it==end) hash[p]=i;
		}
		int t=fast_pow(fast_pow(A,C-2),m);//先求逆元A^(-1),再求A^(-i*m)
		ll tt=B;
		ll ans=-1;
		for(int i=0;i<m;i++)//列舉i 
		{
			it=hash.find(tt);
			if(it!=end)
			{
				ans=m*i+it->second;
				break;
			}
			tt=(tt*t)%C;
		}
		if(ans==-1)
		puts("no solution");
		else
		printf("%lld\n",ans);
	}
	return 0;
}

//莫名超時 
#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#define ll long long
using namespace std;
ll C,A,B;
ll fast_pow(ll a,ll b){
	//求解(a^b)%C的值
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%C;
		b>>=1;
		a=(a*a)%C;
	}
	return ans;
}
int main(){
	while(~scanf("%lld%lld%lld",&C,&A,&B)){
		int m=(int)ceil(sqrt(C));
		map<int,int> hash;
		map<int,int>::iterator it,end=hash.end();
		for(int i=0;i<m;i++)
		{
			//此操作時間複雜度到了m*logm,打表 
			int p=fast_pow(A,i);
			if(hash.count(p)==0) hash[p]=i;
		}
		int t=fast_pow(fast_pow(A,C-2),m);//先求逆元A^(-1),再求A^(-i*m)
		ll tt=B;
		ll ans=-1;
		for(int i=0;i<m;i++)//列舉i 
		{
			if(hash.count(tt))
			{
				ans=m*i+hash[tt];
				break;
			}
			tt=(tt*t)%C;
		}
		if(ans==-1)
		puts("no solution");
		else
		printf("%lld\n",ans);
	}
	return 0;
}
Discrete Logging
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 5835 Accepted: 2604

Description

Given a prime P, 2 <= P < 231, an integer B, 2 <= B < P, and an integer N, 1 <= N < P, compute the discrete logarithm of N, base B, modulo P. That is, find an integer L such that 
    BL == N (mod P)

Input

Read several lines of input, each containing P,B,N separated by a space.

Output

For each line print the logarithm on a separate line. If there are several, print the smallest; if there is none, print "no solution".

Sample Input

5 2 1
5 2 2
5 2 3
5 2 4
5 3 1
5 3 2
5 3 3
5 3 4
5 4 1
5 4 2
5 4 3
5 4 4
12345701 2 1111111
1111111121 65537 1111111111

Sample Output

0
1
3
2
0
3
1
2
0
no solution
no solution
1
9584351
462803587

Hint

The solution to this problem requires a well known result in number theory that is probably expected of you for Putnam but not ACM competitions. It is Fermat's theorem that states 
   B(P-1) == 1 (mod P)

for any prime P and some other (fairly rare) numbers known as base-B pseudoprimes. A rarer subset of the base-B pseudoprimes, known as Carmichael numbers, are pseudoprimes for every base between 2 and P-1. A corollary to Fermat's theorem is that for any m 
   B(-m) == B(P-1-m) (mod P) .
用排序+二分查詢,效率會提高10倍,因為本來陣列就基本有序,所以排序的效率就高了,所以說還是不能剛看時間複雜度,還是要具體問題具體分析。