zoukankan      html  css  js  c++  java
  • 阶与原根学习笔记

    V.阶与原根

    实际上这部分内容在OI中应用很少,但它是一些重要思想以及算法的基础。

    阶是在互质数 \((a,m)\) 间的定义:满足 \(a^n\equiv1\pmod m\) 的最小 \(n\) 被称作 \(a\)\(m\) 的阶,记作 \(\delta_m(a)\)

    明显,在 \(a,m\) 不互质时,\(a\) 的无论多少次幂全都是 \(\gcd(a,m)\) 的倍数,故不可能取到 \(1\);而当互质时,依据扩展欧拉定理,必有 \(a^{\varphi(m)}\equiv1\pmod m\),但也不排除存在更小的 \(n\)

    阶有什么性质呢?

    性质1.\(a^1,a^2,\dots,a^{\delta_a(m)}\)\(m\) 两两不同余。

    如果两个数 \(a^i,a^j\) 同余的话(不妨设 \(i<j\)),则 \(a^i\times(a^{j-i}-1)\equiv0\pmod m\),而 \(a^i\not\equiv0\pmod m\),故只有可能 \(a^{j-i}-1\equiv0\pmod m\),即 \(a^{j-i}\equiv0\pmod m\),而 \(j-i\) 一定是 \(<\delta_a(m)\) 的,故此时 \(\delta_a(m)\) 就不是最小的满足 \(a^n\equiv1\pmod m\)\(n\) 了,与前提矛盾。

    性质2.若 \(a^n\equiv1\pmod m\),则 \(\delta_m(a)|n\)

    \(f(n)=a^n\bmod m\) 这个函数,依照性质1,其最短循环节是 \(\delta_m(a)\)。故所有模 \(m\)\(1\) 的数一定只存在于 \(\delta_m(a)\) 倍数的位置。


    一个数 \(g\) 被称作 \(m\) 的原根,当且仅当 \(\delta_m(g)=\varphi(m)\)

    显然,其前提条件是 \(\gcd(g,m)=1\),不然 \(\delta_m(g)\) 不存在。

    原根判定定理:\(g\)\(m\) 的原根,当且仅当对于一切 \(\varphi(m)\) 的质因数 \(p\),都有 \(g^{\varphi(m)/p}\not\equiv1\pmod m\)

    原根个数定理:若 \(m\) 有原根,则原根个数是 \(\varphi\Big(\varphi(m)\Big)\)

    原根存在定理:\(m\) 有原根,当且仅当 \(m\in\{2,4,p^\alpha,2p^\alpha\}\),其中 \(p\) 是奇质数,\(\alpha\in\N^+\)

    最小原根级别定理:若 \(m\) 有原根,则最小原根是 \(\sqrt[4]{m}\) 级别的。

    什么?你问证明?但是考试也不考证明啊(摊手)

    原根寻找方法:暴力找到最小原根 \(g\),则对于所有与 \(\varphi(m)\) 互质的 \(x\)\(g^x\) 都是 \(m\) 的原根。

    时间复杂度 \(O\Bigg(\log m\Big(\sqrt[4]{m}+\varphi\big(\varphi(m)\big)\Big)+\varphi(m)\Bigg)\)

    总结步骤:

    1. 判断 \(m\) 是否 \(\in\{2,4,p^\alpha,2p^\alpha\}\),不是则直接返回。
    2. 预处理 \(\varphi(m)\) 所有质因数。
    3. 从小到大枚举与 \(m\) 互质的 \(i\),并依次用快速幂检验 \(\varphi(m)\) 所有质因数。
    4. 找到最小原根,枚举所有与 \(\varphi(m)\) 互质的数,求快速幂即可。

    V.I.【模板】原根

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000000;
    int T,n,d,pri[1001000],phi[1001000],mnp[1001000];
    int ksm(int x,int y){
    	int z=1;
    	for(;y;y>>=1,x=1ll*x*x%n)if(y&1)z=1ll*z*x%n;
    	return z;
    }
    void sieve(){
    	for(int i=2;i<=N;i++){
    		if(!pri[i])pri[++pri[0]]=i,phi[i]=i-1,mnp[i]=i;
    		for(int j=1;j<=pri[0]&&i*pri[j]<=N;j++){
    			pri[i*pri[j]]=true,mnp[i*pri[j]]=pri[j];
    			if(i%pri[j])phi[i*pri[j]]=phi[i]*phi[pri[j]];
    			else{phi[i*pri[j]]=phi[i]*pri[j];break;}
    		}
    	}
    }
    vector<int>factorize(int ip){
    	vector<int>ret;
    	while(ip!=1)ret.push_back(mnp[ip]),ip/=mnp[ip];
    	sort(ret.begin(),ret.end()),ret.resize(unique(ret.begin(),ret.end())-ret.begin());
    	return ret;
    }
    bool checkrootexist(int ip){
    	if(n==2||n==4)return true;
    	vector<int>v=factorize(ip);
    	if(v.size()==1&&v[0]!=2)return true;
    	if(ip&1)return false;
    	v=factorize(ip/2);
    	return v.size()==1&&v[0]!=2;
    }
    vector<int>fp;
    bool checkisroot(int ip){
    	for(auto i:fp)if(ksm(ip,phi[n]/i)==1)return false;
    	return true;
    }
    vector<int>res;
    void findallroots(int ip){
    	for(int i=1;i<=phi[n];i++)if(__gcd(i,phi[n])==1)res.push_back(ksm(ip,i));
    	sort(res.begin(),res.end());
    } 
    int main(){
    	sieve();
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d",&n,&d),res.clear();
    		if(!checkrootexist(n)){puts("0\n");continue;}
    		fp=factorize(phi[n]);
    		for(int i=1;;i++)if(__gcd(i,n)==1&&checkisroot(i)){findallroots(i);break;}
    		printf("%d\n",res.size());
    		for(int i=d-1;i<res.size();i+=d)printf("%d ",res[i]);puts("");
    	}
    	return 0;
    }
    

  • 相关阅读:
    移动开发基础(二)touch事件
    js的性能优化
    理解Java的Class.forName()方法
    Linux 串口读写(一)
    PreparedStatement是如何大幅度提高性能的
    简单图像匹配(转)
    共享内存
    Oracle ORA12505, TNS:listener does not currently know of SID given in connect descriptor 解决
    Top Half & Bottom Half
    vue 插件 使用 Echarts 创建图表 (转)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14620919.html
Copyright © 2011-2022 走看看