今天原本是学习离散对数第三类问题,也是最复杂的一类,不过容量太大,几乎是之前所有知识的综合了,所以先发一个重要步骤:解原根。
当考虑问题 时,两边取离散对数,底数需要取a的原根。原根有很多定义,这里不详讲。求原根主要还是搜索,不过由beizer定理有一个优化:
如果不存在 使得 ,其中 是 即欧拉函数的质因子,那么m是p的一个原根。一般我们求最小原根。
简单写了一段代码,但只适用素数,合数的过几天再写。
#include<cstdio>
#include<algorithm>
#include<cmath>
typedef long long ll;
#define MAXN 1000000
ll solu[MAXN];
ll solunum;
ll notpri[MAXN], pri[MAXN];
ll cnt = 0;
ll fast_pow(ll a, ll b, ll p)
{
a %= p;
ll tmp = 1;
while (b)
{
if (b & 1)tmp = (tmp*a) % p;
b >>= 1;
a = (a*a) % p;
}
return tmp;
}
ll inv(ll a, ll p)
{
return fast_pow(a, p - 2, p);
}
//素数初始化
void ini()
{
pri[0] = 2;
for (ll i = 2; i <= MAXN; i++)
{
if (!notpri[i])pri[cnt++] = i;
for (ll j = 0; j < cnt; j++)
{
if (i*pri[j] >= MAXN)break;
notpri[i*pri[j]] = 1;
}
}
}
ll proottable[MAXN];
ll factor[MAXN];//注意memset
ll factornum;
ll proot(ll n)
{
memset(factor, 0, sizeof(factor));
factornum = 0;
ll ncopy = n;
for (ll i = 0; i < cnt; i++)
{
ll flg = 0;
if (pri[i] > n)break;
ll m = pri[i];
while (!(ncopy%m))
{
flg = 1;
ncopy /= m;
}
if (flg)factor[factornum++] = pri[i];
if (ncopy == 1)break;
//if (ncopy / pri[i] < pri[i])
//{
//fac[factornum++] = pri[i];
//break;
//}
}
for (ll i = 2; i < n; i++)
{
ll flg = 1;
for (ll j = 0; j < factornum; j++)
{
ll x = factor[j];
ll yu = fast_pow(i, (n - 1) / factor[j], n);
if (yu == 1)
{
flg = 0;
break;
}
}
if (flg == 1)return i;
}
}
int main()
{
ini();
ll g;
scanf("%lld", &g);
printf("%lld", proot(g - 1));
return 0;
}