原根
这个的用处还是非常大的
- 在模意义下的乘法可以通过原根转换为加法。
- NTT 需要原根。
定义
,且
。则称
是
的一个原根。
是否有原根
只有 ,其中
为奇素数 ,
为正整数。
计算最小原根
这里直接使用最常见的作法。由小到大枚举 。
。那么只需要判断
就可以了,如果有一个为
,则这个数不是
的一个原根。根据一些玄学引论,
的最小原根是
级别的,所以即使
很大,这个的时间复杂度仍然可以接受。
证明
考虑反证,令 而且
。且
。根据欧拉定理
。所以我们可以解一组二元一次方程,根据裴蜀定理
。所以
。所以必定满足至少一组
。这与上文相违背,所以得证。
代码
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0,f = 0;char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = 1;ch = getchar();}
while(isdigit(ch)) {x = x * 10 + ch - '0';ch = getchar();}
return f ? -x : x;
}
const int N = 10101010;
int a[50],n;
int varphi(int x) {
int ans = x;
for(int i = 2;i * i <= x;i++) {
if(x % i) continue;
ans = ans / i * (i - 1) ;
while((x % i) == 0) x = x / i;
}
if(x > 1) ans = ans / x * (x - 1) ;
return ans;
}
int qpow(int a,int b,int mod) {
int x = 1;
for(;b;b>>=1,a = 1LL * a * a % mod) {
if(b & 1) x = 1LL * a * x % mod;
}
return x;
}
int p[N],vis[N];
void solve(int limit) {
for(int i = 2;i <= limit;i++) {
if(!vis[i]) p[++p[0]] = i;
for(int j = 1;j <= p[0] && p[j] * i <= limit;j++) {
vis[i * p[j]] = 1;
if((i % p[j]) == 0) break;
}
}
}
int main() {
solve(1e7);
int T = read();
while(T--) {
memset(a,0,sizeof(a));
n = read();
int phi = varphi(n);
int m = phi,ans = 0;
for(int i = 1;i <= p[0] && p[i] * p[i] <= phi;i++) {
if(phi % p[i]) continue;
a[++a[0]] = p[i];
while((phi % p[i]) == 0) phi = phi / p[i];
}
if(phi > 1) a[++a[0]] = phi;
for(int i = 1;i <= m;i++) {
int check = 0;
for(int j = 1;j <= a[0];j++) {
if(qpow(i,m/a[j],n) == 1) check = 1;
}
if(check == 0) {
ans = i;break;
}
}
cout << ans << endl;
}
}
京公网安备 11010502036488号