【luogu P6091】【模板】原根

【模板】原根

题目链接:luogu P6091

题目大意

多组数据,每次给出 n,求它的所有原根。
为了减少输出,给出一个 d,你只要输出从小到大排之后某些位置的数。

思路

首先对原根不清楚的可以先看看这个:
——>点我<——

然后不知道怎么求最小原根的看这个:
——>点我<——

然后一开始你会想着按着找最小原根的方法,把所有的都找完。
当然,这会超时。

那我们考虑有哪些地方要弄,要优化。
首先,先不说找全部,如果一个数它没有原根,那你就要浪费时间跑全部,就很浪费。
那你考虑求出哪一些数,它是有原根的。

那这里有一个定理,就是如果一个数是原根,那么它肯定是满足这些条件中的其中一个:
它是 1/2/4,或者它是 px/2px。(p 为质数)

然后你 1/2/4 直接标,然后枚举素数,以及它的次方,和它次方的倍数,标记一下就好了。
通过这个方法,你可以快速地判断一个数是否有原根。

然后我们来解决下一个问题,就是你不能直接枚举所有判断是不是原根。
这里给出一个方法,可以通过最小的原根求出所有的原根。
如果你找到了最小的原根 g,那对于所有的 gcd(x,φ(n))gx 都是原根。
那你就把它求出来,排序之后按要求输出即可。

代码

#include<cstdio> #include<algorithm> #define ll long long using namespace std; int T, prime[1000001], n, d, phi[1000001]; int zyz[1000001], ans[1000001]; bool yes[1000001], np[1000001]; void get_prime() {//求质数 for (int i = 2; i <= 1000000; i++) { if (!np[i]) { prime[++prime[0]] = i; } for (int j = 1; j <= prime[0] && i * prime[j] <= 1000000; j++) { np[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } void check_have() {//看某个数时候有原根 yes[1] = 1; yes[2] = 1; yes[4] = 1; for (int i = 1; i <= prime[0]; i++) { ll now = prime[i]; while (now <= 1000000ll) { yes[now] = 1; if (now * 2ll <= 1000000ll) yes[now * 2] = 1; now *= 1ll * prime[i]; } } } void get_phi() {//求phi值 phi[1] = 1; for (int i = 2; i <= 1000000; i++) { if (!np[i]) phi[i] = i - 1; for (int j = 1; j <= prime[0] && i * prime[j] <= 1000000; j++) { if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } } void fj(int now) {//分解质因数 for (int i = 1; prime[i] * prime[i] <= now; i++) if (now % prime[i] == 0) { zyz[++zyz[0]] = prime[i]; while (now % prime[i] == 0) now /= prime[i]; } if (now > 1) zyz[++zyz[0]] = now; } int ksm(ll x, int y, int mo) {//求快速幂 ll re = 1ll; while (y) { if (y & 1) re = (re * x) % mo; x = (x * x) % mo; y >>= 1; } return re; } bool check(int x, int p) {//判断这个数是否是原根 if (ksm(1ll * x, phi[p], p) != 1) return 0; for (int i = 1; i <= zyz[0]; i++) if (ksm(1ll * x, phi[p] / zyz[i], p) == 1) return 0; return 1; } int get_fir(int now) {//找到第一个原根 for (int i = 1; i < now; i++) {//逐个判断,找到就退出 if (check(i, now)) return i; } return 0; } int gcd(int x, int y) {//求最大公因子 if (!y) return x; return gcd(y, x % y); } void get_all(int fir, int p) {//得到所有的原根 int now = 1; for (int i = 1; i <= phi[p]; i++) { now = (now * fir) % p; if (gcd(i, phi[p]) == 1) ans[++ans[0]] = now; } } int main() { get_prime(); check_have(); get_phi(); scanf("%d", &T); for (int times = 1; times <= T; times++) { scanf("%d %d", &n, &d); if (!yes[n]) { printf("0\n\n"); } else { printf("%d\n", phi[phi[n]]); zyz[0] = 0; fj(phi[n]); int fir = get_fir(n); ans[0] = 0; get_all(fir, n); sort(ans + 1, ans + ans[0] + 1);//判断之后按要求输出 for (int i = 1; i <= phi[phi[n]] / d; i++) printf("%d ", ans[i * d]); printf("\n"); } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P6091.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(77)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示