前言

上周在 51nod 交了一些3、4级的题目,然后发现没有写过1级题,

就找到了一道 51nod 1014 \(X^2 \bmod P\) 的题目,当然这题虽然是暴力,但也可以用二次剩余做。

我就顺手看到了 51nod 1039 \(X^3 \bmod P\) (8级题)和 51nod 1038 \(X^A \bmod P\) (7级题)的题目。

本着模数都是质数那就尝试写一写,但在 \(X^3 \bmod P\) 用 BSGS 交一直 \(1.1s\) TLE 过不去,反而交到 \(X^A \bmod P\) 的题目那里就过了。

问了万能的 UOJ 群他们就推荐了三次剩余,就跑得飞快。

PS:这里的N次剩余模数是质数,不是一般情况,一般情况应该更加复杂。

如果只需要一个立方根,也可以在 LOJ 175 模立方根 中提交。


N次剩余(原根,BSGS,扩欧)

要解 \(X^A\equiv B\pmod{P}\),把它尝试转换成 \(A^X\equiv B\pmod{P}\) 的问题那就可以用 BSGS 解决。

考虑 \(P\) 的原根 \(G\),可以让 \([0,P-1]\) 用 \(G^{0\sim P-1}\) 一一对应。

那么相当于 \(G^{kA}\equiv G^{B'}\pmod{P}\),那么先用 BSGS 将 \(B'\) 算出来。

然后相当于解决 \(kA\equiv B'\pmod{P-1}\) 的问题,用扩欧算出所有的 \(k\) 或者判无解。

然后最后的答案就是所有的 \(G^k\)

时间复杂度 \(O(\sqrt P)\)

代码:

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int MOD=70001;
struct Linked_Hash{
struct node{int y,w,next;}E[MOD]; int Et,hs[MOD];
void Clear(){Et=0,memset(hs,-1,sizeof(hs));}
void Insert(int w,int x){E[++Et]=(node){x,w,hs[w%MOD]},hs[w%MOD]=Et;}
int locate(int W){
for (int i=hs[W%MOD];~i;i=E[i].next)
if (E[i].w==W) return E[i].y;
return -1;
}
}ha;
int p,phi,P[MOD],ToT,g,a,m,ans[MOD],tot,prime[MOD],v[MOD],Cnt;
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void FJ(int p){
for (int i=1;prime[i]*prime[i]<=p;++i)
if (p%prime[i]==0){
P[++ToT]=prime[i];
while (p%prime[i]==0) p/=prime[i];
}
if (p>1) P[++ToT]=p;
}
int ksm(int x,int y,int p){
int ans=1;
for (;y;y>>=1,x=1ll*x*x%p)
if (y&1) ans=1ll*ans*x%p;
return ans;
}
bool check(int x,int p){
if (ksm(x,phi,p)!=1) return 0;
for (int i=1;i<=ToT;++i)
if (ksm(x,phi/P[i],p)==1) return 0;
return 1;
}
int Mn_Rt(int p){
for (int i=1;i<p;++i)
if (check(i,p)) return i;
return -1;
}
int BSGS(int a,int b,int mod){
ha.Clear();
int ir=sqrt(mod)+1,now=1;
for (int i=0;i<ir;++i)
ha.Insert(1ll*now*b%mod,i),now=1ll*now*a%mod;
a=now,now=1;
if (!a) return !b?1:-1;
for (int i=0;i<=ir;++i){
int j=ha.locate(now);
if (j>=0&&i*ir>=j) return i*ir-j;
now=1ll*now*a%mod;
}
return -1;
}
int exgcd(int a,int b,int &x,int &y){
if (!b) {x=1,y=0; return a;}
int Gcd=exgcd(b,a%b,y,x);
y-=a/b*x;
return Gcd;
}
int main(){
for (int i=2;i<MOD;++i){
if (!v[i]) prime[++Cnt]=i;
for (int j=1;j<=Cnt&&prime[j]<=MOD/i;++j){
v[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
for (int Test=iut();Test;--Test){
ToT=tot=0,p=iut(),a=iut(),m=iut(),FJ(phi=p-1),g=Mn_Rt(p);
int b=phi,x,y,c=BSGS(g,m,p),Gcd=exgcd(a,b,x,y);
if (c%Gcd) printf("No Solution\n");
else{
x=(1ll*x*(c/Gcd)%phi+phi)%phi;
for (int i=1;i<=Gcd;++i)
x=(x+b/Gcd)%phi,ans[++tot]=ksm(g,x,p);
sort(ans+1,ans+1+tot),tot=unique(ans+1,ans+1+tot)-ans-1;
for (int i=1;i<=tot;++i) print(ans[i]),putchar(i==tot?10:32);
}
}
return 0;
}

二次剩余(Cipolla's Algorithm)

当 \(a=0\) 或者 \(p=2\) 的情况,特判,只考虑奇素数的情况。

若 \(x^2\equiv a\pmod p\) 有解,那么 \(a\) 就是 \(p\) 的二次剩余,否则为非二次剩余。

定义勒让德符号 \(\left(\frac{a}{p}\right)=\begin{cases}1,a是p的二次剩余\\-1,a是p的非二次剩余\end{cases}\)

欧拉判别法:\(\left(\frac{a}{p}\right)=a^{\frac{p-1}{2}}(\gcd(a,p)=1)\)

如果 \(x^2\equiv a\pmod p\) 有解,那么 \(x^{p-1}\equiv 1\pmod p\),那么 \(a^{\frac{p-1}{2}}\equiv 1\pmod p\)

如果无解,考虑将 \(1\sim p-1\) 的数两两配对,使得\(\forall x,y\in[1,p),xy=a\),如果有解,一定会有 \(x\) 与 \(p-x\) 无法配对。

既然配成了 \(\frac{p-1}{2}\) 对,它们的乘积就是 \((p-1)!\equiv -1\pmod p\) 所以 \(a^{\frac{p-1}{2}}\equiv -1\pmod p\)

然后有两个结论用来证明做法的正确性。

  1. 若 \(x^2\equiv a\pmod p\) 有解,那么 \(a\) 的个数为 \(\frac{p-1}{2}\),相当于 \(a\) 与 \(p-a\) 一个有解,一个无解。
  2. \((a+b)^p\equiv a^p+b^p\pmod p\),直接二项式定理展开 \(\binom{p}{i}\) 当且仅当 \(i=0\) 或者 \(i=p\) 时 \(p\) 才能被消去,否则都会被 \(p\) 取模为0。

第一条结论说明随机出一个非二次剩余的期望次数为两次。

考虑随机出一个数 \(A\),使得 \(A^2-a\) 是一个非二次剩余,也就是 \((A^2-a)^{\frac{p-1}{2}}\equiv -1\pmod p\)

令 \(\omega=\sqrt{A^2-a}\),那么它应该是一个虚数,所有的复数都能用 \(x+y\omega\) 表示。

最后的答案 \(x\equiv (A+\omega)^{\frac{p+1}{2}}\pmod p\)

证明就是 \(x^2\equiv (A+\omega)^{p+1}\pmod p\)

后面这个东西因为第二条结论可以变成 \(x^2\equiv (A^p+\omega ^p)(A+\omega)\pmod p\)

因为 \((A^2-a)^{\frac{p-1}{2}}\equiv -1\pmod p\) 也就是 \({\omega}^{p-1}\equiv -1\pmod p\)

最后式子化简成 \(x^2\equiv (A-\omega)(A+\omega)\pmod p\)

因为 \(A^2-{\omega}^2=a\) 所以 \(x^2\equiv a\pmod p\)

那么总结一下做法:

特判 \(a=0\) 或者 \(p=2\) 的情况,然后如果 \(a^{\frac{p-1}{2}}\equiv -1\pmod p\) 那么无解。

然后随机出一个 \(A\),使得 \(A^2-a\) 是 \(p\) 的非二次剩余,那么答案就是 \((A+\omega)^{\frac{p+1}{2}}\)

时间复杂度 \(O(2\log p)\)

代码:

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
typedef long long lll; int n,mod,now;
int mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
struct CP{
lll x,y;
inline CP operator *(const CP &t)const{
return (CP){mo(x*t.x%mod,y*t.y%mod*now%mod),mo(x*t.y%mod,y*t.x%mod)};
}
};
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
CP KSM(CP x,int y){
CP ans=(CP){1,0};
for (;y;y>>=1,x=x*x)
if (y&1) ans=ans*x;
return ans;
}
int ksm(int x,int y){
int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
int Cipolla(int n,int mod){
if (!n) return 0;
if (mod==2) return 1;
if (ksm(n,(mod-1)>>1)==mod-1) return -1;
for (int A=1;;){
A=rand()%(mod-1)+1;
now=mo(1ll*A*A%mod,mod-n);
if (ksm(now,(mod-1)>>1)==mod-1){
CP Ans=KSM((CP){A,1},(mod+1)>>1);
return Ans.x;
}
}
}
int main(){
for (int Test=iut();Test;--Test,putchar(10)){
n=iut(),mod=iut();
int Ans=Cipolla(n,mod);
if (!n||mod==2) print(Ans);
else if (Ans==-1) printf("Hola!");
else{
int _Ans=mod-Ans;
if (Ans>_Ans) swap(Ans,_Ans);
print(Ans),putchar(32),print(_Ans);
}
}
return 0;
}

三次剩余(Peralta Method Extension)

\(a=0\) 和 \(p\leq 3\) 的情况特殊判断。

那么质数就可以在模三意义下分为一和二两种。

众所周知,一个三次单位根为 \(\omega=\frac{\sqrt{3}i-1}{2}\),

那么如果它是一个实数且方程有解,那么就有三个实数解,\(\sqrt{p-3}\) 用二次剩余求出来。

也就是说如果找到一个实数解 \(x\),那么 \(x\omega\) 和 \(x{\omega}^2\) 也是合法的实数解

如果 \(p\bmod 3=2\) 那么有且仅有一解 \(a^{\frac{2p-1}{3}}\),另外两个解都不是实数。

考虑 \(p\bmod 3=1\) 的情况,那么有解当且仅当 \(a^{\frac{p-1}{3}}\equiv 1\pmod p\)

引理:如果 \(k|p-1\),那么 \(a\) 是 \(p\) 的 \(k\) 次剩余当且仅当 \(a^{\frac{p-1}{k}}\equiv 1\pmod p\)

证明:充分性显然,考虑必要性的证明,设 \(p\) 的一个原根为 \(g\) 且 \(g^t=a\),

那么在指数上,\(t\frac{p-1}{k}\equiv 0\pmod{p-1}\) 那也就是要保证 \(k|p-1\) 必要性也可以证明出来。

随机出三个数 \(A,B,C\) 构造多项式 \(f(x)=Ax^2+Bx+C,x^3=a\) 那么 \(f(x)^{p-1}\equiv 1\pmod p\)

那么如果 \(f(x)^{\frac{p-1}{3}}\) 只有一次项 \(B'\) 非0,那么 \((B'x)^3=1\) 即 \(B'=x^{-1}\)

期望次数为9次,所以时间复杂度为 \(O(9\log p)\)

代码:

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
typedef long long lll; int A,mod,now,NOW;
int mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
struct CP{
lll x,y;
inline CP operator *(const CP &t)const{
return (CP){mo(x*t.x%mod,y*t.y%mod*now%mod),mo(x*t.y%mod,y*t.x%mod)};
}
};
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
CP KSM(CP x,int y){
CP ans=(CP){1,0};
for (;y;y>>=1,x=x*x)
if (y&1) ans=ans*x;
return ans;
}
int ksm(int x,int y){
int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
int Cipolla(int n,int mod){
if (!n) return 0;
if (mod==2) return 1;
if (ksm(n,(mod-1)>>1)==mod-1) return -1;
for (int A=1;;){
A=rand()%(mod-1)+1;
now=mo(1ll*A*A%mod,mod-n);
if (ksm(now,(mod-1)>>1)==mod-1){
CP Ans=KSM((CP){A,1},(mod+1)>>1);
return Ans.x;
}
}
}
struct Three{
lll f[3];
inline Three operator *(const Three &t)const{
Three F=(Three){0,0,0};
for (int i=0;i<3;++i)
for (int j=0;j<3;++j)
if (i+j<3) F.f[i+j]+=f[i]*t.f[j]%mod;
else F.f[i+j-3]+=f[i]*t.f[j]%mod*NOW%mod;
for (int i=0;i<3;++i) F.f[i]%=mod;
return F;
}
};
Three ksM(Three x,int y){
Three ans=(Three){1,0,0};
for (;y;y>>=1,x=x*x)
if (y&1) ans=ans*x;
return ans;
}
Three Peralta(int A,int mod){
if (!A) return (Three){0,-1,-1};
if (mod<4) return (Three){A,-1,-1};
if (mod%3==2) return (Three){ksm(A,(2*mod-1)/3),-1,-1};
if (ksm(A,(mod-1)/3)!=1) return (Three){-1,-1,-1};
int omega=1ll*(Cipolla(mod-3,mod)-1)*ksm(2,mod-2)%mod;
Three X; NOW=A;
while (1){
X=(Three){rand()%mod,rand()%mod,rand()%mod};
Three Ans=ksM(X,(mod-1)/3);
if (!Ans.f[0]&&!Ans.f[2]&&Ans.f[1]){
Ans.f[1]=ksm(Ans.f[1],mod-2);
Ans.f[0]=Ans.f[1]*omega%mod;
Ans.f[2]=Ans.f[0]*omega%mod;
return Ans;
}
}
}
int main(){
for (int Test=iut();Test;--Test,putchar(10)){
mod=iut(),A=iut();
Three Ans=Peralta(A,mod);
if (Ans.f[0]==-1) printf("No Solution");
else if (Ans.f[1]==-1) print(Ans.f[0]);
else{
sort(Ans.f,Ans.f+3);
for (int i=0;i<3;++i)
print(Ans.f[i]),putchar(32);
}
}
return 0;
}

后记

二次剩余来自 beginend的二次剩余Cipolla算法学习小记

三次剩余来自 skywalkert的寻找模质数意义下的二次剩余与三次剩余

N次剩余小记的更多相关文章

  1. xml小记1

    xml小记1 关于边框的实现 这是一个比较简单的东西,但是今天莫名的低效率,在这上面花了比较多的时间.之前有咨询过同学如何实现单向的边框,他们采用的方法是调用别人的接口. 我采用的方法如下: < ...

  2. 算法-动态规划DP小记

    算法-动态规划DP小记 动态规划算法是一种比较灵活的算法,针对具体的问题要具体分析,其宗旨就是要找出要解决问题的状态,然后逆向转化为求解子问题,最终回到已知的初始态,然后再顺序累计各个子问题的解从而得 ...

  3. JavaScript小记

    JavaScript小记 1. 简介 1. 语言描述 JavaScript 是一门跨平台.面向对象的弱类型动态脚本编程语言 JavaScript 是一门基于原型.函数先行的语言 JavaScript ...

  4. Java:AQS 小记-1(概述)

    Java:AQS 小记-1(概述) 概述 全称是 Abstract Queued Synchronizer(抽象队列同步器),是阻塞式锁和相关的同步器工具的框架,这个类在 java.util.conc ...

  5. Java:异常小记

    Java:异常小记 对 Java 中的 异常 ,做一个微不足道的小小小小记 Error 和 Exception 相同点: Exception 和Error 都是继承了 Throwable 类,在 Ja ...

  6. 【动图解释】关系数据库de关系代数小记

    本文章在 Github 撰写,同时在 我的博客 进行了发布. 最近学数据库概论学到了关系数据库的关系代数了.哎嘛,真的把我整晕了,尤其是关系代数的使用,很容易让人被蒙在鼓里. 对我来说槽点最大的莫过于 ...

  7. [原]Paste.deploy 与 WSGI, keystone 小记

    Paste.deploy 与 WSGI, keystone 小记 名词解释: Paste.deploy 是一个WSGI工具包,用于更方便的管理WSGI应用, 可以通过配置文件,将WSGI应用加载起来. ...

  8. android计算每个目录剩余空间丶总空间以及SD卡剩余空间

    ublic class MemorySpaceCheck { /** * 计算剩余空间 * @param path * @return */ public static String getAvail ...

  9. 二次剩余、三次剩余、k次剩余

    今天研究了一下这块内容...首先是板子 #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  10. MySql 小记

    MySql  简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...

随机推荐

  1. 03-Redis系列之-高级用法详解

    慢查询 生命周期 我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询. 慢查询发生在第三阶段 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素 两个配置 slowl ...

  2. 关于Python中math 和 decimal 模块的解析与实践

    本文分享自华为云社区<Python数学模块深度解析与实战应用>,作者: 柠檬味拥抱. 在Python中,math 和 decimal 模块是处理数学运算的重要工具.math 提供了一系列常 ...

  3. macOS通过ssh使用PEM登录

    在win上面可以使用XSHELL来登录类似于亚马逊这样的安全服务器,在mac上面就可以使用系统自带的命令工具来连接 使用命令 ssh -i key.pem [server] 如下: ssh -i ke ...

  4. 【Azure Data Lake Storage】如何才能保留Blob中的文件目录结构(即使文件夹中文件数量为0的情况下)?

    问题描述 在使用Azure Storage Account Blob时候,发现当文件夹中的Blob(文件)被全部输出后,文件夹也会消失? 为什么它不能像Windows系统中的文件夹一样,即使是一个空文 ...

  5. 【Azure 事件中心】Azure Event Hub客户端遇见 Expired Heartbeat 错误

    问题描述 Azure Event Hub 在消费数端中,经常性遇见 Expired Heartbeat 错误 (consumer-xxxxxxxxxxxxx-c84873c6c828e8df6c843 ...

  6. 【Azure 云服务】云服务(经典)迁移到云服务(外延支持)的八个问题

    问题一:云服务( 经典)迁移到外延支持云服务是否需要停机? 通过平台的迁移工具(即验证.准备.提交)进行迁移没有停机时间.但是如果需要准备满足迁移条件,如删除对等互联,使用其他vnet资源则需要额外的 ...

  7. 【Azure Spring Cloud】部署Azure spring cloud 失败

    问题描述 使用Azure CLI指令部署Azure Spring Cloud项目失败,错误消息提示没有安装"azure.storage.blob"模块 问题分析 根据错误提示,是p ...

  8. 如何实现十亿级离线 CSV 导入 Nebula Graph

    本文首发于 Nebula Graph Community 公众号 本次实践是基于业务需求及后续扩展,通过技术选型确定了 Nebula Graph 图数据库,首先需要验证 Nebula Graph 数据 ...

  9. 【Filament】基于物理的光照(PBR)

    1 前言 ​ 自定义Blinn Phong光照模型中实现了基础的自定义光照,与现实的光照还是有些差别,本文将实现更逼真的光照效果,即基于物理的光照(PBR). ​ 读者如果对 Filament 不太熟 ...

  10. dart特殊符号语法(一)

    许久没有写博客.浏览以往的博客,是那么稚嫩.就让它们当成成长的照片吧.重新开始操起这个记录的爱好,一方面把它当成可以查阅的资料,方便自己:另一方面希望有帮助于人.由于个人能力,认知有限,如读者发现有纰 ...