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)):
5.转换成扩展欧几里得的形式求出所有解即可:
上代码:
#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;
}