原根
原根以前没学懂,今天重新学了一下。
19.07.02update:忘了,又重新学了一下。
19.07.09update:耶我记住了。
定义
先引出阶的定义:
若\((a, n) = 1\),则满足\(a^r \equiv 1 (mod \ \ n)\)的最小整数\(r\),称为\(a\)模\(n\)的阶。
首先\(r\)是显然存在的,因为根据费马小定理,有\(a ^ {\varphi(n)} \equiv 1 (mod \ \ n)\),那么\(r \leqslant \varphi(n)\)。
当\(r = \varphi(n)\)的时候,称\(a\)为模\(n\)的一个原根。
性质
对于原根\(g\),\(g ^ 0, g ^ 1, g ^ 2 \ldots g ^ {p - 2}\)恰好与\([1, p - 1]\)一一对应。在[SDOI2015]序列统计这道题上有妙用。
求法
暴力的求法:
因为原根一般比较小,所以从2开始枚举,然后对于每一个枚举的\(g\),再从\(1\)到\(\varphi(n) - 1\)枚举\(i\),根据定义,若\(g ^ i \equiv 1 (mod \ \ n)\),那么\(g\)就不是原根。
改进:
实际上没必要枚举所有\(i\),只要枚举\(\varphi(n)\)的因数即可。
即如果对于所有的\(i \ \ (i | \varphi(n))\)都有\(g ^ i \not\equiv 1 (mod \ \ n)\),必然对于任意一个\(j \ \ (j \not\mid \varphi(n))\),都有\(g ^ j \not\equiv 1 (mod \ \ n)\)。
证明:原根--zhouyuheng2003。
于是代码就很好写了
51nod上竟然有例题:原根
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
//const int maxn = ;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In ll quickpow(ll a, ll b, ll mod)
{
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) ret = ret * a % mod;
return ret;
}
In ll phi(ll n)
{
ll ret = n;
for(int i = 2; i * i <= n; ++i)
{
if(n % i) continue;
ret = ret / i * (i - 1);
while(n % i == 0) n /= i;
}
if(n > 1) ret = ret / n * (n - 1);
return ret;
}
int p[1000], pcnt = 0;
In ll G(ll m)
{
ll Phi = phi(m);
pcnt = 0;
for(int i = 2; i * i <= Phi; ++i)
if(Phi % i == 0)
{
p[++pcnt] = i;
if(Phi / i != i) p[++pcnt] = Phi / i;
}
for(int g = 2; g <= Phi; ++g)
{
bool flg = 1;
if(quickpow(g, Phi, m) ^ 1) continue;
for(int i = 1; i <= pcnt && flg; ++i)
if(quickpow(g, p[i], m) == 1) flg = 0;
if(flg) return g;
}
}
int main()
{
ll m = read();
write(G(m)), enter;
return 0;
}