LINK:原根
再复习一下原根 防止考场上要NTT求原根的时候不会求...
这道题要求求出n之内的所有原根 根据原根的定义。
原根指 若x对于模n的阶为phi(n)且(1leq xleq n) 那么称x为n的原根.
暴力做法枚举x 枚举phi(n)的因数 看其是否同余1.
复杂度nsqrt(n)左右。
考虑更快的做法 (去年省选骗分过样例也用了这个做法
考虑求出最小的原根g(暴力,但是很快. 对于剩下的原根都可以表示成g^k,条件为(k,phi(n))==1.(显然。
于是就做完了。值得一提的是我们还需要知道一个数字是否有原根。
有原根的必要充要条件:2,4,p^k,2*p^k.其中p为奇质数。
const int MAXN=1000010;
int T,top,cnt,n,maxx;
int p[MAXN],v[MAXN],phi[MAXN],vis[MAXN],s[MAXN],an[MAXN];
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline void prepare()
{
phi[1]=1;
for(int i=2;i<=maxx;++i)
{
if(!v[i]){p[++top]=v[i]=i;phi[i]=i-1;}
rep(1,top,j)
{
if(maxx/i<p[j])break;
int ww=i*p[j];
v[ww]=p[j];
if(v[i]==p[j]){phi[ww]=phi[i]*p[j];break;}
phi[ww]=phi[i]*(p[j]-1);
}
}
vis[2]=1;vis[4]=1;
rep(2,top,i)
{
for(ll j=1*p[i];j<=maxx;j=j*p[i])vis[j]=1;
for(ll j=2*p[i];j<=maxx;j=j*p[i])vis[j]=1;
}
}
inline int ksm(ll b,int p,int mod)
{
ll cnt=1;
while(p)
{
if(p&1)cnt=cnt*b%mod;
b=b*b%mod;p=p>>1;
}
return cnt;
}
inline void fj(int x)
{
top=0;
for(int i=1;p[i]*p[i]<=x;++i)
if(x%p[i]==0)
{
s[++top]=p[i];
while(x%p[i]==0)x/=p[i];
}
if(x>1)s[++top]=x;
}
inline int check(int x,int p)
{
if(ksm(x,phi[p],p)!=1)return 0;
rep(1,top,i)
if(ksm(x,phi[p]/s[i],p)==1)return 0;
return 1;
}
inline int getg(int p)
{
rep(1,p-1,i)if(check(i,p))return i;
return 0;
}
inline void get_ans(int g,int p)
{
cnt=0;ll ww=1;
rep(1,phi[p],i)
{
ww=ww*g%p;
if(gcd(i,phi[p])==1)an[++cnt]=ww;
}
}
int main()
{
freopen("1.in","r",stdin);
get(T);maxx=1000000;prepare();
while(T--)
{
get(n);int d;get(d);
if(vis[n])
{
fj(phi[n]);
get_ans(getg(n),n);
sort(an+1,an+1+cnt);
put(cnt);
rep(1,cnt/d,i)printf("%d ",an[i*d]);
}
else puts("0");
puts("");
}
return 0;
}