原根

原根以前没学懂,今天重新学了一下。
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;
}
posted @ 2019-04-20 20:58  mrclr  阅读(505)  评论(0编辑  收藏  举报