题目看这里
经典的n次剩余问题,用到很多数论知识点
1.扩展gcd
2.原根
3.离散对数
4.n次剩余
说一下这个算法的流程
首先,我们的方程为xn=a(mod m)xn=a(mod m) m为质数
那么,我们首先要找m的原根g,这里g要满足的性 质就是对于任意i<j<m,gi≠gj(mod m)任意i<j<m,gi≠gj(mod m)
那么,任何一个x<mx<m都可以和一个gxgx对应,而且是唯一对应
我们用离散对数求出一个t使得a=gta=gt
把原来的方程化为n∗y=t(mod m−1)n∗y=t(mod m−1)
这个就可以用扩展gcd求解了
还是比较简洁的,就是跑得很慢,主要是离散对数
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define LL long long
using namespace std;
int w[50010],t=0; bool vis[100010];
inline LL pow(LL x,LL k,LL M,LL s=1){
for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
return s;
}
inline LL exgcd(LL a,LL b,LL& x,LL& y){
if(b){
LL r=exgcd(b,a%b,y,x);
y-=x*(a/b); return r;
} else { x=1; y=0; return a; }
}
inline void init(){
for(int i=2;i<=100000;++i){
if(!vis[i]) w[++t]=i;
for(int j=1,k;(k=i*w[j])<=100000;++j){
vis[k]=1;
if(i%w[j]==0) break;
}
}
}
inline int gRt(LL p){
LL s=p-1,r[40]={0},c=0;
for(int i=1,j;w[i]*w[i]<=s;++i)
if(s%(j=w[i])==0) for(r[++c]=j;s%j==0;s/=j);
if(s>1) r[++c]=s;
for(int g=1;;){
begin:
for(int i=1;i<=c;++i)
if(pow(g,(p-1)/r[i],p)==1){ ++g; goto begin; }
return g;
}
}
struct P{
LL x,y;
inline bool operator< (const P& b){
return x==b.x?y<b.y:x<b.x;
}
} s[100010],a;
inline LL log(LL x,LL n,LL m){
LL q=sqrt(m)+1,c=1,inv,r;
for(int i=0;i<q;++i){
s[i]=(P){c,i}; c=c*x%m;
}
sort(s,s+q); inv=pow(c,m-2,m); c=1;
for(int i=0;i<q;++i){
r=n*c%m;
a=*lower_bound(s,s+q,(P){r,-1});
if(a.x==r) return i*q+a.y;
c=c*inv%m;
}
return -1;
}
int W[100000];
inline int Ndx(int M,int n,int a){
if(!a) return puts("0");
int g=gRt(M); LL m=log(g,a,M);
if(m<0) return puts("No Solution");
LL x,y,r=exgcd(n,--M,x,y),d;
if(m%r) return puts("No Solution");
x=x*(m/r)%M; d=M/r;
for(int i=0;i<r;++i){
x=(x+d+M)%M;
W[i]=pow(g,x,M+1);
}
sort(W,W+r);
for(int i=0;i<r;++i) printf("%d ",W[i]); puts("");
}
int main(){
int T,N,M,A; init();
for(scanf("%d",&T);T--;Ndx(M,N,A)) scanf("%d%d%d",&M,&N,&A);
}