首页 > 其他分享 >题解 P6091 【模板】原根

题解 P6091 【模板】原根

时间:2023-07-17 21:45:34浏览次数:52  
标签:gcd 原根 题解 ll varphi P6091 ord operatorname

题解太高深,自己整理一份。

阶的定义: 若 \(\gcd(a,m)=1\),则使 \(a^l\equiv1\pmod{m}\) 的最小的 \(l\) 称为 \(a\) 关于模 \(m\) 的阶,记为 \(\operatorname{ord}_m a\)。

阶的性质:

根据欧拉定理,\(a^{\varphi(m)}\equiv 1\pmod{m}\),所以 \(\operatorname{ord}_m a\mid \varphi(m)\)。

进一步的,若 \(a^x\equiv1\pmod{m}\),则 \(\operatorname{ord}_m a\mid x\)。

由此可见,阶相当于最小循环节。

证明: \(\operatorname{ord}_m a^t=\frac{\operatorname{ord}_m a}{\gcd(\operatorname{ord}_m a,t)}\)。

设 \(\operatorname{ord}_m a^t=l\),则 \(a^{t\cdot l}\equiv1\pmod{m}\)。

因为 \(\operatorname{ord}_m a\mid t\cdot l\),\(t\mid t\cdot l\),所以 \(t\cdot l=\operatorname{lcm}(\operatorname{ord}_m a,t)\)。

所以 \(l=\frac{\operatorname{lcm}(\operatorname{ord}_m a,t)}{t}=\frac{\operatorname{ord}_m a\cdot t}{\gcd(\operatorname{ord}_m a,t)\cdot t}=\frac{\operatorname{ord}_m a}{\gcd(\operatorname{ord}_m a,t)}\)。

原根的定义: 当 \(\operatorname{ord}_m a=\varphi(m)\) 时,称 \(a\) 为关于 \(m\) 的原根。

原根的性质:

只有 \(2,4,p_i,2\cdot p_i\) 有原根,其中 \(p_i\) 为奇素数。

当 \(g\) 为原根时 \(g,g^2,g^3,...,g^{\varphi(m)}\) 构成模 \(m\) 意义下的既约剩余系,而因为 \(\gcd(g,m)=1\),所以 \(\gcd(g^x,m)=1\),且剩余系中的各个数互不相等。

考虑反证法。若有两个数 \(g^x,g^y\) 相等,则说明 \(g^{y-x}\equiv1\pmod{m}\),由于 \(y-x<\varphi(m)\),所以 \(g\) 不为原根,矛盾。

那么,可能的原根只能在 \(g,g^2,g^3,...,g^{\varphi(m)}\) 中。

如果 \(g^t\) 满足 \(\operatorname{ord}_m g^t=\varphi(m)\),则 \(\frac{\operatorname{ord}_m g}{\gcd(\operatorname{ord}_m g,t)}=\varphi(m)\),即 \(\frac{\varphi(m)}{\gcd(\varphi(m),t)}=\varphi(m)\)。所以当且 \(\gcd(t,\varphi(m))=1\) 时,\(g^t\) 为 \(m\) 的原根。

如何求 \(g\):

显然,\(g^{\varphi(m)}\equiv1\pmod{m}\) 且不存在 \(x<\varphi(m)\) 满足 \(g^x\equiv1\pmod{m}\)。

而如果存在这样的 \(x\),则必然有 \(x\mid \varphi(m)\)。

所以我们可以通过质因数分解找到 \(\varphi(m)\) 的所有质因子 \(p_i\),枚举每一个 \(p_i\) 判断 \(g^{\frac{\varphi(m)}{p_i}}\equiv1\pmod{m}\) 即可。

具体实现:

  1. 线性筛素数,预处理 \(\varphi\) 函数,\(O(n)\)。

  2. 分解 \(\varphi(n)\),\(O(\sqrt n)\)。

  3. 枚举找到 \(g\),\(O(n^{0.25}\log^2 n)\)。

  4. 找出所有原根并排序,\(O(n\log n)\)。

综上,复杂度 \(O(Tn\log n)\)。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int T,n,d,p[N],phi[N],cnt=0,tot=0,ans[N];
bool vis[N],rt[N];
vector<int>yin;
void init(){
  phi[1]=1;
  for(ll i=2;i<N;++i){
    if(!vis[i]){
      p[++cnt]=i;
      phi[i]=i-1;
    }
    for(int j=1;j<=cnt&&i*p[j]<N;++j){
      vis[i*p[j]]=1;
      if(i%p[j]==0){
        phi[i*p[j]]=phi[i]*p[j];
        break;
      }
      phi[i*p[j]]=phi[i]*(p[j]-1);
    }
  }
  rt[2]=rt[4]=1;
  for(int i=2;i<=cnt;++i){
    for(ll j=p[i];j<N;j*=p[i])rt[j]=1;
    for(ll j=p[i]*2;j<N;j*=p[i])rt[j]=1;
  }
}
void fen(int x){
  for(ll i=2;i*i<=x;++i){
    if(x%i==0){
      yin.push_back(i);
      while(x%i==0)x/=i;
    }
  }
  if(x>1)yin.push_back(x);
}
int ksm(ll x,ll y){
  ll res=1;
  while(y){
    if(y&1)res=res*x%n;
    y>>=1;
    x=x*x%n;
  }
  return res;
}
int getg(){
  for(int i=1;i<=n;++i){
    if(ksm(i,phi[n])!=1)continue;
    bool flag=1;
    for(int j=0;j<yin.size();++j){
      if(ksm(i,phi[n]/yin[j])==1){
        flag=0;
        break;
      }
    }
    if(flag)return i;
  }
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int main(){
  cin>>T;
  init();
  while(T--){
    scanf("%d%d",&n,&d);yin.clear();tot=0;
    if(rt[n]){
      fen(phi[n]);
      ll g=getg();
      ll fac=1;
      for(int i=1;i<=phi[n];++i){
        fac=fac*g%n;
        if(gcd(i,phi[n])==1)ans[++tot]=fac;
      }
      sort(ans+1,ans+tot+1);
    }
    printf("%d\n",tot);
    for(int i=d;i<=tot;i+=d)printf("%d ",ans[i]);
    cout<<endl;
  }
  return 0;
}

标签:gcd,原根,题解,ll,varphi,P6091,ord,operatorname
From: https://www.cnblogs.com/HQJ2007/p/17561326.html

相关文章

  • 题解 CF417D
    \(m\le20\),状压DP。首先可以根据每个人的\(k\)从小到大排序。定义\(f_{i,j}\)表示考虑到第\(i\)个人,完成了\(j\)状态的题目,不考虑显示器所需的最小价格。转移显然为\(f_{i,j|s_i}=\min(f_{i-1,j}+x_i)\)。最终答案为\(ans=\min\limits_{i=1}^{n}f_{i,S}+b\cdotk_......
  • 题解 CF985E
    贪心+DP。先从小到大排序。定义\(f_i\)表示序列\([1,i]\)能否分组。转移为\(f_i|=f_j[a_i-a_j\led,j\lei-k+1]\)。右区间可以直接算出来,左区间可以二分或根据单调性弄个指针。定义\(sum_i=\sum\limits_{t=1}^{i}f_t\),前缀和减一下判断是否为正即可。复杂度\(O(n\lo......
  • 题解 P4815 [CCO2014] 狼人游戏
    看题目限制,可以发现如果将机器人作为点,指控和保护关系作为边,可以建出一个森林,就下来就是传统的树形背包了。设\(f_{i,j,0/1}\)表示当前点为\(i\),子树内有\(j\)个狼人,当前点是否为狼人的方案数。初始化:\(f_{u,0,0}=f_{u,1,1}=1\)当前点为狼:指控:\(f_{u,j,1}=f_{u,j-k,......
  • 题解 [ABC276F] Double Chance
    很容易想到分类。我们可以把\(1\)到\(i-1\)的球分为两类,一类是权值小于\(val_i\),另一类是权值大于\(val_i\)。对于第一类,\(sum\)加上小于\(val_i\)的球的个数乘以\(val_i\)。对于第二类,\(sum\)加上所有大于\(val_i\)的球的权值。这显然可以用两个树状数组维护。......
  • 题解 P5426 [USACO19OPEN]Balancing Inversions G
    来一篇简单易懂的良心题解。由于数值不是\(0\)就是\(1\),我们可以考虑将逆序对的统计方式化简。以左区间为例,设\(x\)为\(1\)的个数,\(p_i\)为第\(i\)个\(1\)的坐标,则逆序对个数为\(\sum\limits_{i=1}^{x}n-p_i-(x-i)\)。也就是\((n-x)\cdotx+\frac{x\cdot(x+1)}{......
  • 题解 Friendly Spiders
    FriendlySpiders带有技巧的最短路。如果\(u\)能到\(v\),说明\(\gcd(u,v)>1\),也就是有相同因子。所以我们考虑对于每个数\(u\),向他的所有质因子连一条长度为\(1\)的边,这样我们从\(u\)到\(v\)需要走两步,最终答案除以\(2\)即可。每次遇到一个新的因子,都要新建节点。......
  • 题解 The Human Equation
    TheHumanEquation思维题。我们考虑每次\(a\)数组加一减一对于其前缀和\(sum\)的影响。可以发现,假设相邻两次加一和减一的位置分别为\(l\)和\(r\),那么\(sum\)在\([l,r)\)内会加一。先减后加也同理。所以,一次加减操作相当于将\(sum\)若干段连续序列加一或减一。......
  • 题解 [ARC153B] Grid Rotations
    [ARC153B]GridRotations有思维含量的一题。我们横纵坐标分开考虑,对于每一个矩形,每次操作会使其内部元素的横坐标上下对调。纵坐标也同理,左右对调。而这种反转操作我们显然可以直接用两棵文艺平衡树维护,复杂度\(O(n\logn)\)。标程的做法更巧妙一下。我们可以把一条链收尾......
  • 题解 Yet Another Minimization Problem
    YetAnotherMinimizationProblem神仙题。第一眼看上去就是DP。定义\(f_{i,j}\)表示当前点\(i\),分\(j\)段的最小费用。\(f_{i,j}=\min(f_{i,j},f_{k,j-1}+val_{k+1,i})\)然后发现复杂度\(O(n^2k)\),直接T飞,需要优化。我们发现\(j\)那一维可以滚掉,也就是只考虑第......
  • 题解 P4322 [JSOI2016]最佳团体
    P4322[JSOI2016]最佳团体分数规划+树形背包。可以根据推荐关系建出一颗树,然后如果选了一点,则该点到根上的所有点都必须选。二分\(mid\),定义每个结点的权值,然后判断选\(k+1\)个节点的最大值是否大于\(0\)。设\(f_{i,j}\)为当前节点\(i\),在其子树内选了\(j\)个节点,最......