解原根問題在上週一直tle沒有解決,今天折騰了半天(製造了大量access violation),坑點在於很多判斷的條件。
即使按照質因數優化的方法,如果直接遍歷2~ 的話,複雜度還是很高,夠tle幾十次了。原根的重要性質(模p):
這個性質用來快速判斷枚舉的g是否爲原根。以下性質形成這之前的剪枝策略:
1 當整數可寫成 或者 時,它纔有原根,其中p是素數,n是整數。
2 若g爲模p的原根,則所有原根爲 並且滿足 ,事實上原根個數等於 。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long int
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a%b);
}
int fast_pow(int a,int b,int p)
{
a %= p;
ll tmp = 1;
while (b)
{
if (b & 1)tmp = (ll)tmp*a%p;
b >>= 1;
a = (ll)a*a%p;
}
return tmp;
}
#define N 1000000+10
int phi[N], pri[N];
bool notpri[N];
int cnt;
void ini()
{
phi[1] = 1;
for (int i = 2; i < N; i++)
{
if (!notpri[i])
{
pri[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; j < cnt; j++)
{
if (i*pri[j] >= N)break;
notpri[i*pri[j]] = 1;
if (i%pri[j] != 0)
{
phi[i*pri[j]] = phi[i] * (pri[j] - 1);
}
else
{
phi[i*pri[j]] = phi[i] * pri[j];
break;
}
}
}
}
bool check(int a)//剪枝
{
if (a == 2 || a == 4)return 1;
if ((a & 1) ^ 1)a >>= 1;//這行是學習的其他博客的,最後一位是0
//偶數一定不是【除2以外】質數的冪
for (int i = 1; i < cnt; i++)
{
int p = pri[i];
if (p > a)break;
if (a%p == 0)
{
while (a%p == 0)a /= p;
if (a == 1)return 1;
else return 0;
}
}
return 0;
}
int np;
int pf[1005];
int rt(int a)//解原根
{
//if (!check(a))return -1;
memset(pf, 0, sizeof(pf));
np = 0;//初始化
int cpphi=phi[a];
int k = ceil(sqrt(phi[a]*1.0));
for (int i = 0; i < cnt; i++)//分解質因數
{
if (pri[i] > k)
{
if (cpphi != 1)pf[np++] = cpphi;
break;
}
if (cpphi%pri[i] == 0)
{
pf[np++] = pri[i];
while (cpphi%pri[i] == 0)cpphi /= pri[i];
if (cpphi == 1)break;
}
}
for (int i = 2; i < a; i++)
{
if (fast_pow(i, phi[a], a) == 1)
{
bool flg = 1;
for (int j = 0; j < np; j++)
{
if (fast_pow(i, phi[a] / pf[j], a) == 1)
{
flg = 0;
break;
}
}
if(flg)return i;
}
}
return -1;
}
void solve(int a)
{
if (a == 2)
{
printf("1\n");
return;
}
bool T = check(a);
if (!T) { printf("-1\n"); return; }
int pr[N];
int nump = 0;
int g = rt(a);
if(g==-1)
{
printf("-1\n");
return;
}
ll gp = g;
for (int p = 1; p < phi[a]; gp = gp*g%a, p++)
{
if (gcd(p, phi[a]) == 1)pr[nump++] = gp;
}
sort(pr, pr + nump);
for (int j = 0; j < nump-1; j++)
{
printf("%d ", pr[j]);
}
printf("%d\n", pr[nump-1]);
return;
}
int main()
{
int a;
ini();
while (scanf("%d", &a) != EOF)
{
solve(a);
}
return 0;
}