11D_Beyonder
2020-07-10 20:50:38
移步博客
要定义原根,先要引入数论中阶的概念。
设
说明:描述阶的性质时,默认
性质1
证明 设
首先证明充分性。设
接下来证明必要性。设
证毕。
推论1
证明 由欧拉定理,
性质2 设
证明 首先
接下来用反证法证明
证毕。
性质3 设
证明 首先证明充分性。设
接下来证明必要性。不妨设
证毕。
性质4 令
证明 下面用反证法证明。
假设
证毕。
性质5 设
证明 令
证毕。
性质6 令
证明 令
由性质1,
根据阶的定义,
证毕。
性质7 设
证明 由阶的定义,
证毕。
性质8 设
证明 令
根据阶的定义,
由最小公倍数的定义,有
综上所述,
证毕。
性质9 设
证明 令
由
因为
综上所述,
证毕。
设
不加证明地给出:模
不加证明地给出:若
定理 设
略证 由原根的判定定理,对于任意
若
根据欧拉函数定义,满足条件
可以证明,最小原根是不大于
下面就有了本题的代码
#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
#define ll long long
#define N 1000002
using namespace std;
int cnt;//质数个数
bool vis[N];//vis[i]=1表示i为合数
int prime[N>>1];//质数
int phi[N];//欧拉函数
vector<int>prime_factor;//质因子
vector<int>ans;//所有模n的原根
//-------------------------------------------------------
//欧拉筛线性得到欧拉函数
void get_phi(int n)
{
phi[1]=1;
int i,j;
for(i=2;i<=n;i++)
{
if(!vis[i])
{
phi[i]=i-1;
prime[++cnt]=i;
}
for(j=1;j<=cnt&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
//-------------------------------------------------------
//-------------------------------------------------------
//快速幂
inline int qpow_mod(ll a,int b,int mod)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
//-------------------------------------------------------
//-------------------------------------------------------
//分解质因数
inline void divide(int x)
{
int i;
for(i=2;i*i<=x;i++)
{
if(x%i==0)
{
prime_factor.push_back(i);
while(x%i==0) x/=i;
}
}
if(x>1) prime_factor.push_back(x);
}
//-------------------------------------------------------
//-------------------------------------------------------
//利用原根存在定理
bool exist(int n)
{
if(n==2||n==4) return 1;
if(n%2==0) n/=2;//猜测为奇素数幂的二倍
int i;
//检验是否为奇素数的幂
for(i=2;prime[i]<=n;i++)
{
if(n%prime[i]==0)
{
while(n%prime[i]==0) n/=prime[i];
//若n存在原根
//i为唯一质因子
return n==1;
}
}
return 0;
}
//-------------------------------------------------------
//-------------------------------------------------------
int main()
{
get_phi(1000000);//预处理1~1000000的欧拉函数
int _;
for(cin>>_;_;_--)
{
int n,d;
scanf("%d%d",&n,&d);
//首先检验n是否存在原根
if(exist(n))
{
prime_factor.clear();
ans.clear();
//对phi[n]分解质因数
divide(phi[n]);
int i,j;
int g;
//---------------------------------------------------------
//找到最小原根g
for(i=1;;i++)
{
bool is=1;
if(__gcd(i,n)!=1) continue;//i不存在模n的阶
for(j=0;j<prime_factor.size();j++)
{
//--------------------------------------------------
//原根判定
//对phi[n]的所有质因子p
//i^(phi[n]/p)%n!=1;
if(qpow_mod(i,phi[n]/prime_factor[j],n)==1)
{
is=0;
break;
}
//--------------------------------------------------
}
if(is)//找到最小原根
{
g=i;
break;
}
}
//---------------------------------------------------------
//---------------------------------------------------------
//枚举指数s
//若s与phi[n]互质
//g^s为原根
int s;
ll power=1;
for(s=1;ans.size()<phi[phi[n]];s++)
{
power=power*g%n;
if(__gcd(phi[n],s)==1) ans.push_back(power);
}
sort(ans.begin(),ans.end());
printf("%d\n",phi[phi[n]]);
//---------------------------------------------------------
for(i=d-1;i<phi[phi[n]]/d*d&&i<ans.size();i+=d)
{
//if(i) putchar(' ');
printf("%d ",ans[i]);
}
putchar('\n');
}
else puts("0\n");
}
return 0;
}