正题

题目链接:https://www.luogu.com.cn/problem/P6091


题目大意

给出一个数\(p\),求出它的所有在\([0,p]\)的原根。


解题思路

原根的定义,\(\delta_p(a)\)表示一个最小的\(n\)使得\(a^n\equiv1(mod\ p)\),若\(gcd(a,p)=1\)且\(\delta_p(a)=\varphi(p)\)则\(a\)为\(p\)的一个原根。

两个个结论就是一个数有原根当且仅当它为\(2,4,p^a,2p^a\)(其中\(p\)为奇质数,\(a\in N^+\))。还有若\(g\)表示最小正原根,那么其他原根可以被表示为\(g^k\% p(\ gcd(\varphi(p),k)=1\ )\)。

这两个结论在洛谷题解都有详细证明,这里就不多赘述了。

那么考虑如何求出最小正原根,因为原根的数量大约有\(\varphi(\varphi (p))\)个,所以密集度比较高,据说最小正原根约是\(O(n^{2.5})\)级别的。

所以考虑直接枚举,但是我们判定的时候肯定不能从\(1\sim \varphi(p)\)枚举来判断。

我们还需要用到一个结论就是如果对于\(gcd(a,p)=1\)且\(a^k\equiv 1(mod\ p)\)(也就是\(k\)是\(a\)模\(n\)的阶),那么有\(k|\varphi(p)\)。所以我们需要判定\(\varphi(p)\)的所有因子?看起来还是很大,但是我们显然有\(a^k\equiv 1(mod\ p)\)那么\(a^{kx}\equiv1(mod\ p)\)其中\(x\in N^+\)。所以我们只需要枚举\(\frac{\varphi(p)}{k}\)(其中\(k\)是\(\varphi(p)\)的质因子)即可,因为这些数包含了其他数的倍数。

时间复杂度\(O(n^{2.5}\log n+n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=1e6+10;
ll T,n,d,cnt,phi[N],pri[N];
bool v[N],rt[N];
vector<int> q;
void prime(){
phi[1]=1;
for(ll i=2;i<N;i++){
if(!v[i])pri[++cnt]=i,phi[i]=i-1;
for(ll j=1;j<=cnt&&i*pri[j]<N;j++){
v[i*pri[j]]=1;
if(i%pri[j]==0){
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*phi[pri[j]];
}
}
rt[2]=rt[4]=1;
for(ll i=2;i<=cnt;i++){
for(ll j=1;j<N;j*=pri[i])rt[j]=1;
for(ll j=2;j<N;j*=pri[i])rt[j]=1;
}
return;
}
ll power(ll x,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=ans*x%p;
x=x*x%p;b>>=1;
}
return ans;
}
ll gcd(ll x,ll y)
{return (!y)?x:gcd(y,x%y);}
void dec_phi(ll x){
for(ll i=1;i<=cnt&&pri[i]*pri[i]<=x;i++)
if(x%pri[i]==0){
q.push_back(pri[i]);
while(x%pri[i]==0)x/=pri[i];
}
if(x!=1)q.push_back(x);
return;
}
bool check(ll x){
if(power(x,phi[n],n)!=1)return 0;
for(ll i=0;i<q.size();i++)
if(power(x,phi[n]/q[i],n)==1)
return 0;
return 1;
}
signed main()
{
scanf("%lld",&T);
prime();
while(T--){
scanf("%lld%lld",&n,&d);q.clear();
if(!rt[n]){printf("0\n\n");continue;}
dec_phi(phi[n]);ll g=1;
while(!check(g))g++;
ll tmp=1;q.clear();
for(ll i=1;i<=phi[n];i++){
tmp=tmp*g%n;
if(gcd(phi[n],i)==1)
q.push_back(tmp);
}
printf("%lld\n",q.size());
sort(q.begin(),q.end());
for(ll i=1;i<=q.size()/d;i++)
printf("%lld ",q[i*d-1]);
putchar('\n');
}
return 0;
}

P6091-[模板]原根的更多相关文章

  1. x^a=b(mod c)求解x在[0,c-1]上解的个数模板+原根求法

    /************************************* 求解x^a=b(mod c) x在[0,c-1]上解的个数模板 输入:1e9>=a,b>=1,1e9>= ...

  2. FFT模板 生成函数 原根 多项式求逆 多项式开根

    FFT #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> ...

  3. hdu 4992 Primitive Roots 【求原根模板】

    题目链接 大题流程: 判定是否有原根->求出最小原根->利用最小原根找出全部原根 #include<bits/stdc++.h> using namespace std; ty ...

  4. luogu P6091 原根

    LINK:原根 再复习一下原根 防止考场上要NTT求原根的时候不会求... 这道题要求求出n之内的所有原根 根据原根的定义. 原根指 若x对于模n的阶为phi(n)且\(1\leq x\leq n\) ...

  5. 多项式FFT相关模板

    自己码了一个模板...有点辛苦...常数十分大,小心使用 #include <iostream> #include <stdio.h> #include <math.h& ...

  6. uestc_retarded 模板

    虽然这个队,以后再也没有了,但是他的模板,是永垂不朽的![误 #include <ext/pb_ds/priority_queue.hpp> __gnu_pbds::priority_qu ...

  7. 多项式细节梳理&模板(多项式)

    基础 很久以前的多项式总结 现在的码风又变了... FFT和NTT的板子 typedef complex<double> C; const double PI=acos(-1); void ...

  8. 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)

    注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...

  9. FFT/NTT总结+洛谷P3803 【模板】多项式乘法(FFT)(FFT/NTT)

    前言 众所周知,这两个东西都是用来算多项式乘法的. 对于这种常人思维难以理解的东西,就少些理解,多背板子吧! 因此只总结一下思路和代码,什么概念和推式子就靠巨佬们吧 推荐自为风月马前卒巨佬的概念和定理 ...

  10. NTT模板

    NTT(快速数论变换)用到的各种素数及原根: https://blog.csdn.net/hnust_xx/article/details/76572828 NTT多项式乘法模板 #include&l ...

随机推荐

  1. Spark Core核心----RDD常用算子编程

    1.RDD常用操作2.Transformations算子3.Actions算子4.SparkRDD案例实战 1.Transformations算子(lazy) 含义:create a new data ...

  2. mzy对于枚举的理解

    关于enum,其实就是简化了的class,功能就是提供一个个独立的.特定含义的常量! 在JDK5.0之前我们想模拟enum的功能,只能使用自定义类的形式: 1.首先私有化构造方法,让外部不能new对象 ...

  3. 寻找最短路径Dijkstra算法

    1 /** 2 * 1.对于T中的每个顶点u,找到u的具有最小权重的连接边.所有到u的连接边都存储在queues.get(u)中.queues.get(u).peek()返回拥有最小权值 3 * 的连 ...

  4. 跨域@RequestBody@RequestParam 和JSON.stringify

  5. ES6扩展——数值扩展

    1.0o代表八进制 0b代表二进制 ,通过Number()可转为10进制: //0o 0O octanary八进制 //0b 0B binary二进制 console.log(0o16); //14 ...

  6. 剑指offer 计划1(栈与队列)---java

    1.1.题目1 剑指 Offer 09. 用两个栈实现队列 1.2.解法 解法如题目所说.定义两个栈.这里假设第一个栈为a,第二个栈为b. 实现两个函数增加尾和删除头. 增加即直接push入第一个栈. ...

  7. Python中的变量以及变量的命名

    1.变量的定义 在 python 中,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建 等号(=)用来给变量赋值 =左边是一个变量名 =右边是存储在变量中的值 变量名=值 变量定义之后,后续就 ...

  8. Linux下chkconfig

    1.chkconfig命令用于检查,设置系统的各种服务! 2.chkconfig语法 chkconfig [--add] [--del] [--list] [系统服务] 或 chkconfig [-- ...

  9. 源码解析.Net中Host主机的构建过程

    前言 本篇文章着重讲一下在.Net中Host主机的构建过程,依旧延续之前文章的思路,着重讲解其源码,如果有不知道有哪些用法的同学可以点击这里,废话不多说,咱们直接进入正题 Host构建过程 下图是我自 ...

  10. Git 系列教程(10)- 仓库别名

    Git 别名 前言 Git 并不会在你输入部分命令时自动推断出你想要的命令 如果不想每次都输入完整的 Git 命令,可以通过 git config 文件来轻松地为每一个命令设置一个别名 $ git c ...