正题
题目链接:https://www.luogu.com.cn/problem/P6091
题目大意
给出一个数ppp,求出它的所有在[0,p][0,p][0,p]的原根。
解题思路
原根的定义,δp(a)\delta_p(a)δp(a)表示一个最小的nnn使得an≡1(modp)a^n\equiv1(mod\ p)an≡1(mod p),若gcd(a,p)=1gcd(a,p)=1gcd(a,p)=1且δp(a)=φ(p)\delta_p(a)=\varphi(p)δp(a)=φ(p)则aaa为ppp的一个原根。
两个个结论就是一个数有原根当且仅当它为2,4,pa,2pa2,4,p^a,2p^a2,4,pa,2pa(其中ppp为奇质数,a∈N+a\in N^+a∈N+)。还有若ggg表示最小正原根,那么其他原根可以被表示为gk%p(gcd(φ(p),k)=1)g^k\% p(\ gcd(\varphi(p),k)=1\ )gk%p( gcd(φ(p),k)=1 )。
这两个结论在洛谷题解都有详细证明,这里就不多赘述了。
那么考虑如何求出最小正原根,因为原根的数量大约有φ(φ(p))\varphi(\varphi (p))φ(φ(p))个,所以密集度比较高,据说最小正原根约是O(n2.5)O(n^{2.5})O(n2.5)级别的。
所以考虑直接枚举,但是我们判定的时候肯定不能从1∼φ(p)1\sim \varphi(p)1∼φ(p)枚举来判断。
我们还需要用到一个结论就是如果对于gcd(a,p)=1gcd(a,p)=1gcd(a,p)=1且ak≡1(modp)a^k\equiv 1(mod\ p)ak≡1(mod p)(也就是kkk是aaa模nnn的阶),那么有k∣φ(p)k|\varphi(p)k∣φ(p)。所以我们需要判定φ(p)\varphi(p)φ(p)的所有因子?看起来还是很大,但是我们显然有ak≡1(modp)a^k\equiv 1(mod\ p)ak≡1(mod p)那么akx≡1(modp)a^{kx}\equiv1(mod\ p)akx≡1(mod p)其中x∈N+x\in N^+x∈N+。所以我们只需要枚举φ(p)k\frac{\varphi(p)}{k}kφ(p)(其中kkk是φ(p)\varphi(p)φ(p)的质因子)即可,因为这些数包含了其他数的倍数。
时间复杂度O(n0.25logn)O(n^{0.25}\log n)O(n0.25logn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=1e6+10;
ll T,n,d,cnt,phi[N],pri[N];
bool v[N],rt[N];
vector<int> q;
void prime(){phi[1]=1;for(ll i=2;i<N;i++){if(!v[i])pri[++cnt]=i,phi[i]=i-1;for(ll j=1;j<=cnt&&i*pri[j]<N;j++){v[i*pri[j]]=1;if(i%pri[j]==0){phi[i*pri[j]]=phi[i]*pri[j];break;}phi[i*pri[j]]=phi[i]*phi[pri[j]];}}rt[2]=rt[4]=1;for(ll i=2;i<=cnt;i++){for(ll j=1;j<N;j*=pri[i])rt[j]=1;for(ll j=2;j<N;j*=pri[i])rt[j]=1;}return;
}
ll power(ll x,ll b,ll p){ll ans=1;while(b){if(b&1)ans=ans*x%p;x=x*x%p;b>>=1;}return ans;
}
ll gcd(ll x,ll y)
{return (!y)?x:gcd(y,x%y);}
void dec_phi(ll x){for(ll i=1;i<=cnt&&pri[i]*pri[i]<=x;i++)if(x%pri[i]==0){q.push_back(pri[i]);while(x%pri[i]==0)x/=pri[i];}if(x!=1)q.push_back(x);return;
}
bool check(ll x){if(power(x,phi[n],n)!=1)return 0;for(ll i=0;i<q.size();i++)if(power(x,phi[n]/q[i],n)==1)return 0;return 1;
}
signed main()
{scanf("%lld",&T);prime();while(T--){scanf("%lld%lld",&n,&d);q.clear();if(!rt[n]){printf("0\n\n");continue;}dec_phi(phi[n]);ll g=1;while(!check(g))g++;ll tmp=1;q.clear();for(ll i=1;i<=phi[n];i++){tmp=tmp*g%n;if(gcd(phi[n],i)==1)q.push_back(tmp);}printf("%lld\n",q.size());sort(q.begin(),q.end());for(ll i=1;i<=q.size()/d;i++)printf("%lld ",q[i*d-1]);putchar('\n');}return 0;
}