原根

原根以前没学懂,今天重新学了一下。
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 otequiv 1 (mod n)),必然对于任意一个(j (j otmid varphi(n))),都有(g ^ j otequiv 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;
}
原文地址:https://www.cnblogs.com/mrclr/p/10742576.html