luogu P6091 原根

LINK:原根

再复习一下原根 防止考场上要NTT求原根的时候不会求...

这道题要求求出n之内的所有原根 根据原根的定义。

原根指 若x对于模n的阶为phi(n)且\(1\leq x\leq n\) 那么称x为n的原根.

暴力做法枚举x 枚举phi(n)的因数 看其是否同余1.

复杂度nsqrt(n)左右。

考虑更快的做法 (去年省选骗分过样例也用了这个做法

考虑求出最小的原根g(暴力,但是很快. 对于剩下的原根都可以表示成g^k,条件为(k,phi(n))==1.(显然。

于是就做完了。值得一提的是我们还需要知道一个数字是否有原根。

有原根的必要充要条件:2,4,pk,2*pk.其中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;
}
posted @ 2020-03-23 22:20  chdy  阅读(163)  评论(0编辑  收藏  举报