HDU——3930(N次剩餘)

There is a way of encryption in the Digital planet. If the number x, who lives in M area, K layer, then it will change its name to x ^ K mod M, that is newx = x ^ k mod m. Now the Digital Kingdom wants to make a program, which can find all the original number of a name living in some area, some layer. If there is no solution, output -1.

Input

There are multiply test cases. Each test case contains there integers k, m , newx ,(0 <= newx , m ,k <= 1.5*10^15) , m is a prime number.

Output

For each test case, you should output some lines, the format of the first line is: “caseN:” (N is the label of test case). Then next lines each contain a number x, output x as ascending order. (0 <= x < m)

Sample Input

1 5 4
2 13 8
3 13 8

Sample Output

case1:
4
case2:
-1
case3:
2
5
6

原根的性質:
1.如果一個數有原根,那麼它一共有φ(φ(m)) 個原根。
2.如果p爲素數,那麼素數p一定存在原根,並且模p的原根的個數爲φ(p−1) 個。

這個題用不到哦~~~

題意:給出k,m,newx的值,求方程x^k(mod m)=newx的所有解,其中m爲素數。

題解:

以下 "==" 表示恆等

1.素數一定存在原根root,求出root

2.根據原根性質,一定存在1<= w <=m-1 使root^w==newx(mod m)   w通過BSGS求出

3.得到x^k==root^w(mod m)

4.由離散對數的性質得到(兩邊取log(root)):klog_r_o_o_tx\equiv w (mod\;\varphi(m) )

5.轉換成擴展歐幾里得的形式求出所有解即可:k(log_r_o_o_tx)+\varphi (m)y=w

上代碼:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <tr1/unordered_map>
using namespace std::tr1;
using namespace std;
typedef long long ll; 
const ll MAX = 1e7+4;
bool vis[MAX];
ll pr[MAX],sp[MAX];
ll ans[MAX];
ll mul(ll a,ll b,ll c){//快速乘 
    ll ans=0;
    while(b){
        if(b&1) ans=(ans+a)%c;
		b>>=1;
        a=(a<<1)%c;
    }
    return ans;
}
ll quick(ll a,ll b,ll c){//快速冪 
    ll ans=1;     
    a=a%c;   
    while(b!=0){  
        if(b&1) ans=mul(ans,a,c);   
        b>>=1;   
        a=mul(a,a,c);  
    }  
    return ans;  
} 
void getpr(){ //歐拉篩素數 時間複雜度O(n) 
	ll cnt=0;
	vis[0]=vis[1]=true;
	for (ll i = 2; i < MAX;i++){
		if(!vis[i]) pr[cnt++]=i;
		for (ll j = 0; j < cnt&& i*pr[j]< MAX;j++){
			vis[i*pr[j]]=true;
			if(i%pr[j]==0) break;
		}
	}
}
ll get_euler(ll n){ // 歐拉函數模板
    ll res=n,a=n;
    for (ll i = 2; i*i<=a;i++){
        if(a%i==0){
            res=res/i*(i-1);
            while(a%i==0) a=a/i;
        }
    }
    if(a>1) res=res/a*(a-1);
    return res;
}
ll tot;
void divide(ll n){//唯一分解 
	tot=0;
	for (ll i = 0;pr[i]*pr[i] <= n;i++){
		if(n%pr[i]==0){
			sp[tot++]=pr[i];
			while(n%pr[i]==0) n/=pr[i];
		}
	}
	if(n>1){
		sp[tot++]=n;
	}
}
ll getroot(ll n,ll nn){//求原根 
	divide(n);
	ll root=-1;
	for (ll i = 2; i < nn;i++){
		bool flag=1;
		for (ll j = 0; j < tot;j++){
			ll t=(nn-1)/sp[j];
			if(quick(i,t,nn)==1){
				flag=0;
				break;
			}
		}
		if(flag){
			root=i;
			return root;//這裏return 求的是最小原根
		}
	}
	return root;//最大原根,這個題找完用最大原根會T
}
ll ex_gcd(ll a, ll b, ll &x, ll &y){//擴展歐幾里得模板
	if(b==0){
		x=1,y=0;
		return a;
	}
	ll p=ex_gcd(b,a%b,y,x);
	y-=a/b*x;
	return p;
}
void exgcd(ll a,ll &x,ll b,ll &y){//ax+by=1
    if(!b){
        x=1;y=0;
        return ;
    }
    exgcd(b,y,a%b,x);
    y-=a/b*x;
}
ll inverse(ll x,ll y){//x^(-1)(mod y) <=> x*x^(-1)+y*k=1
    ll inv_x,k;
    exgcd(x,inv_x,y,k);
    return (inv_x%y+y)%y;
}
ll BSGS(ll a,ll b,ll c){//a^x=b(mod c)
    for(ll x=0,pow_a_x=1%c;x<=100;++x){
        if(pow_a_x==b)return x;
        pow_a_x=(long long)pow_a_x*a%c;
    }
    ll base_count=0,D=1;
    while(1){
        ll d=__gcd(a,c);
        if(d==1)break;
        if(b%d)return -1;
        b/=d;c/=d;
        D=(long long)D*(a/d)%c;
        ++base_count;
    }
    b=(long long)b*inverse(D,c)%c;
    ll n=sqrt(c);
    unordered_map<ll,ll>hash_table;
    ll pow_a_j=1;
    for (ll j = 1; j <= n;++j){
        pow_a_j=(long long)pow_a_j*a%c;
        hash_table[(long long)pow_a_j*b%c]=j;
    }
    ll pow_a_n=pow_a_j,pow_a_in=1,max_i=(c+n-1)/n;
    for (ll i = 1; i <= max_i;++i){
        pow_a_in=(long long)pow_a_in*pow_a_n%c;
        if(hash_table.count(pow_a_in)) return i*n-hash_table[pow_a_in]+base_count;
    }
    return -1;
}
int main(){
	getpr();
	int cas=1;
	ll newx,m,k;
	while(~scanf("%lld%lld%lld",&k,&m,&newx)){
		printf("case%d:\n",cas++);
		ll mm=get_euler(m);
		ll root=getroot(mm,m);//求模數的原根
		ll w=BSGS(root,newx,m);
		ll x,y;
		ll gcd=ex_gcd(k,mm,x,y);
		if(w%gcd){//擴展歐幾里得判斷無解
			puts("-1");
			continue;
		}
		ll kk=w/gcd;
		x*=kk;
		ll rr=mm/gcd;
		x=(x%rr+rr)%rr;//x的最小值
		ans[0]=quick(root,x,m);
		for (ll i = 1; i < gcd;i++){//gcd個就可以,後面就循環了
			x=x+rr;
			ans[i]=quick(root,x,m);
		}
		sort(ans,ans+gcd);//注意從小到大輸出
		for (int i = 0; i < gcd;i++){
			printf("%lld\n",ans[i]);
		}
	}
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章