记录一些琐碎的数学知识(也可能不是数学)。以下内容重点放在记结论,以便作者复习。慢慢补充吧,东西会比较杂。。
最大公约数
由公约数的基本性质可得:
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
扩展欧几里得
求解关于
x
,
y
x,y
x,y的二元一次方程:
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)
当等式右端不是
g
c
d
(
a
,
b
)
gcd(a,b)
gcd(a,b)的倍数时,方程无解。
考虑求解:
b
x
1
+
(
a
%
b
)
y
1
=
g
c
d
(
b
,
a
%
b
)
=
g
c
d
(
a
,
b
)
bx_1+(a\%b)y_1=gcd(b,a\%b)=gcd(a,b)
bx1+(a%b)y1=gcd(b,a%b)=gcd(a,b)
假设已经求出了
x
1
,
y
1
x_1,y_1
x1,y1。转化:
b
x
1
+
(
a
−
⌊
a
b
⌋
∗
b
)
y
1
=
g
c
d
(
a
,
b
)
bx_1+(a-\lfloor \frac{a}{b} \rfloor *b)y_1=gcd(a,b)
bx1+(a−⌊ba⌋∗b)y1=gcd(a,b)
a
∗
y
1
+
b
∗
(
x
1
−
⌊
a
b
⌋
∗
y
1
)
=
g
c
d
(
a
,
b
)
a*y_1+b*(x_1-\lfloor \frac{a}{b} \rfloor*y_1)=gcd(a,b)
a∗y1+b∗(x1−⌊ba⌋∗y1)=gcd(a,b)
于是有:
x
=
y
1
,
y
=
x
1
−
⌊
a
b
⌋
∗
y
1
x=y_1,y=x_1-\lfloor \frac{a}{b} \rfloor*y_1
x=y1,y=x1−⌊ba⌋∗y1
当递归到最后一层,有:
a
=
g
c
d
(
a
,
b
)
,
b
=
0
a=gcd(a,b),b=0
a=gcd(a,b),b=0,于是此时
x
=
1
,
y
=
0
x=1,y=0
x=1,y=0。
注意一下代码中
x
x
x和
y
y
y的螺旋走位。
inline void exgcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x),y-=a/b*x;
}
中国剩余定理
求解一组线性同余方程,其中满足模数两两互质。
其实就是对于每一组考虑消去其它方程的影响。
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\begin{cases} x\equiv a_1\ \ (mod \ m_1) \\ x\equiv a_2\ \ (mod \ m_2) \\ \;\;\;\; \vdots \\ x\equiv a_n\ \ (mod \ m_n) \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡a1 (mod m1)x≡a2 (mod m2)⋮x≡an (mod mn)
令
M
=
∏
m
i
M=\prod m_i
M=∏mi,
T
i
=
M
m
i
T_i=\frac{M}{m_i}
Ti=miM,考虑求解
r
i
r_i
ri满足:
T
i
∗
r
i
≡
1
(
m
o
d
m
i
)
⟺
a
i
∗
T
i
∗
r
i
≡
a
i
(
m
o
d
m
i
)
T_i*r_i\equiv 1\;\;\;(mod \ m_i) \Longleftrightarrow a_i*T_i*r_i\equiv a_i\;\;\;(mod \ m_i)
Ti∗ri≡1(mod mi)⟺ai∗Ti∗ri≡ai(mod mi)
而同时,这里有
a
i
∗
T
i
∗
r
i
≡
0
(
m
o
d
m
k
)
,
k
≠
i
,
k
∈
[
1
,
n
]
a_i*T_i*r_i\equiv 0\ \ \ (mod\ m_k),k \not=i,k \in[1,n]
ai∗Ti∗ri≡0 (mod mk),k=i,k∈[1,n]
于是
x
x
x有解:
x
0
=
∑
i
=
1
n
a
i
∗
T
i
∗
r
i
x_0=\sum_{i=1}^{n} a_i*T_i*r_i
x0=∑i=1nai∗Ti∗ri,通解有
x
=
x
0
+
k
∗
M
,
k
∈
Z
x=x_0+k*M,k\in \Z
x=x0+k∗M,k∈Z。
模板,比较坑,要龟速乘。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int k,a[N],b[N],x,y,mul=1,ans=0;
inline void turn(int &x,int mod){x=(x%mod+mod)%mod;}
inline void exgcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x),y-=a/b*x;
}
inline int Mul(int a,int b,int ret=0){
for(;b;b>>=1,a=(a+a)%mul)
if(b&1) (ret+=a)%=mul;
return ret;
}
signed main(){
scanf("%lld",&k);
for(int i=1;i<=k;++i) scanf("%lld",a+i);
for(int i=1;i<=k;++i) scanf("%lld",b+i),mul*=b[i];
for(int i=1;i<=k;++i){
int tmp=mul/b[i];
exgcd(tmp,b[i],x,y),turn(x,b[i]),turn(a[i],b[i]);
ans+=Mul(Mul(a[i],tmp),x),turn(ans,mul);
}cout<<ans<<'\n';
}
扩展中国剩余定理
同上,但不满足模数两两互质。此时
T
i
∗
r
i
≡
1
(
m
o
d
m
i
)
T_i*r_i\equiv 1\;(mod \ m_i)
Ti∗ri≡1(mod mi)可能会无解,因为没有互质的条件。
考虑两两合并:
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⟺
{
x
=
a
1
+
p
∗
m
1
x
=
a
2
+
q
∗
m
2
\begin{cases} x\equiv a_1\ \ (mod \ m_1)\\ x\equiv a_2\ \ (mod \ m_2) \end{cases} \Longleftrightarrow \begin{cases} x= a_1+p*m_1\\ x= a_2+q*m_2 \end{cases}
{x≡a1 (mod m1)x≡a2 (mod m2)⟺{x=a1+p∗m1x=a2+q∗m2
将右侧两式相减得到一个二元一次方程组的形式:
p
∗
m
1
−
q
∗
m
2
=
a
2
−
a
1
①
p*m_1-q*m_2=a_2-a_1 \ \ \ \ \ ①
p∗m1−q∗m2=a2−a1 ①
令
x
=
p
a
2
−
a
1
g
c
d
(
m
1
,
m
2
)
,
y
=
−
q
a
2
−
a
1
g
c
d
(
m
1
,
m
2
)
x=\frac{p}{\frac{a_2-a_1}{gcd(m_1,m_2)}},y=\frac{-q}{\frac{a_2-a_1}{gcd(m_1,m_2)}}
x=gcd(m1,m2)a2−a1p,y=gcd(m1,m2)a2−a1−q,考虑求解:
x
∗
m
1
+
y
∗
m
2
=
g
c
d
(
m
1
,
m
2
)
②
x*m_1+y*m_2=gcd(m_1,m_2) \ \ \ \ \ ②
x∗m1+y∗m2=gcd(m1,m2) ②
首先,如果
g
c
d
(
m
1
,
m
2
)
∤
a
2
−
a
1
gcd(m_1,m_2) \not| a_2-a_1
gcd(m1,m2)∣a2−a1,显然①式是无整数解的。否则一定有解。
利用
e
x
g
c
d
exgcd
exgcd把②式求解出来之后,得到
x
,
y
x,y
x,y,然后
x
x
x和
y
y
y分别乘上
a
2
−
a
1
g
c
d
(
m
1
,
m
2
)
\frac{a_2-a_1}{gcd(m_1,m_2)}
gcd(m1,m2)a2−a1得到
p
,
−
q
p,-q
p,−q,随便取
p
p
p或
−
q
-q
−q代入上面大括号右侧式子(
p
p
p和
−
q
-q
−q代出来是一样的)得到一个满足上述同余式的
x
0
x_0
x0。于是有:
{
x
≡
a
1
≡
x
0
(
m
o
d
m
1
)
x
≡
a
2
≡
x
0
(
m
o
d
m
2
)
\begin{cases} x\equiv a_1 \equiv x_0\ \ (mod \ m_1)\\ x\equiv a_2 \equiv x_0\ \ (mod \ m_2) \end{cases}
{x≡a1≡x0 (mod m1)x≡a2≡x0 (mod m2)
接下来证明:
对于两个非零整数
a
,
b
a,b
a,b,且有整数
x
,
y
x,y
x,y,满足
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)(这是一定存在的):如果
a
∣
n
,
b
∣
n
a|n,b|n
a∣n,b∣n,则
l
c
m
(
a
,
b
)
∣
n
lcm(a,b)|n
lcm(a,b)∣n
记
a
′
=
a
g
c
d
(
a
,
b
)
,
b
′
=
b
g
c
d
(
a
,
b
)
a'=\frac{a}{gcd(a,b)},b'=\frac{b}{gcd(a,b)}
a′=gcd(a,b)a,b′=gcd(a,b)b,那么有:
n
=
n
(
a
x
+
b
y
)
g
c
d
(
a
,
b
)
=
n
a
′
x
+
n
b
′
y
n=\frac{n(ax+by)}{gcd(a,b)}=na'x+nb'y
n=gcd(a,b)n(ax+by)=na′x+nb′y
又有
a
∣
n
,
b
∣
n
⟹
a
b
′
∣
n
b
′
,
a
′
b
∣
n
a
′
⟹
a
b
′
∣
n
b
′
y
,
a
′
b
∣
n
a
′
x
⟹
l
c
m
(
a
,
b
)
∣
n
a
′
x
+
n
b
′
y
a|n,b|n \Longrightarrow ab'|nb',a'b|na' \Longrightarrow ab'|nb'y,a'b|na'x \Longrightarrow lcm(a,b)|na'x+nb'y
a∣n,b∣n⟹ab′∣nb′,a′b∣na′⟹ab′∣nb′y,a′b∣na′x⟹lcm(a,b)∣na′x+nb′y
即:
l
c
m
(
a
,
b
)
∣
n
lcm(a,b)|n
lcm(a,b)∣n
上面有:
m
1
∣
x
−
x
0
,
m
2
∣
x
−
x
0
⟹
l
c
m
(
m
1
,
m
2
)
∣
x
−
x
0
m_1|x-x_0,m_2|x-x_0 \Longrightarrow lcm(m_1,m_2)|x-x_0
m1∣x−x0,m2∣x−x0⟹lcm(m1,m2)∣x−x0
即:
x
−
x
0
≡
0
(
m
o
d
l
c
m
(
m
1
,
m
2
)
)
⟹
x
≡
x
0
(
m
o
d
l
c
m
(
m
1
,
m
2
)
)
x-x_0\equiv 0\ \ (mod \ lcm(m_1,m_2)) \Longrightarrow x \equiv x_0\ (mod \ lcm(m_1,m_2))
x−x0≡0 (mod lcm(m1,m2))⟹x≡x0 (mod lcm(m1,m2))
于是这样一个一个合并即可。
模板,注意代码实现里,①式是先把
m
1
m_1
m1,
m
2
m_2
m2和
a
2
−
a
1
a_2-a_1
a2−a1的
g
c
d
(
m
1
,
m
2
)
gcd(m_1,m_2)
gcd(m1,m2)除掉。后面把
p
p
p代入
x
x
x的方程时,再把
m
1
m_1
m1的
g
c
d
(
m
1
,
m
2
)
gcd(m_1,m_2)
gcd(m1,m2)乘回去。不然要
W
A
WA
WA,可能是爆
l
o
n
g
l
o
n
g
longlong
longlong。还有无解的时候不能直接
b
r
e
a
k
break
break掉,因为还要读入。。
#include<cstdio>
#define int long long
#define re register
using namespace std;
inline int gcd(int a,int b){return (b)?gcd(b,a%b):a;}
//ax+by=gcd(a,b)
inline void exgcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x),y-=a/b*x;
}
int n,m1,a1,m2,a2,p,q;
signed main(){
while(~scanf("%lld",&n)){
scanf("%lld%lld",&m1,&a1);
bool flag=1;
for(int re i=2;i<=n;++i){
scanf("%lld%lld",&m2,&a2);
int g=gcd(m1,m2),lcm=m1*m2/g,A=a2-a1;
if(A%g!=0){flag=0;continue;}
m1/=g,m2/=g,A/=g,exgcd(m1,m2,p,q),p=(p*A%m2+m2)%m2;
a1=(a1+p*m1*g)%lcm,m1=lcm;
}
if(!flag) puts("-1");
else printf("%lld\n",a1);
}
}
高斯消元
解决
n
n
n元一次方程组。复杂度
O
(
n
3
)
O(n^3)
O(n3)。
只要记住一个一个地消元即可,红色是用来消元的位置。
红色下方消为0,消完之后往上求解即可。
下图为用第
i
i
i行第
i
i
i个消元。第
j
j
j行的每个系数整体要减去第
i
i
i行的每个系数乘以
a
[
j
]
[
i
]
a
[
i
]
[
i
]
\frac{a[j][i]}{a[i][i]}
a[i][i]a[j][i],然而
a
[
i
]
[
i
]
=
0
a[i][i]=0
a[i][i]=0怎么办?换成一个第
i
i
i列非
0
0
0的行即可。
还有一点,就是取对应列最大的一行作为主元来消元可以减小精度误差,洛谷的题解里似乎有解释。
如果选出的主元等于0,说明它下面的系数都为0,在有解的情况下,主元可取任意值,它前面的元素随它的变化而变化,有无穷多解。相当于是少了一个方程。
若某行的系数均为
0
0
0而常数项非
0
0
0,则无解。下面代码只判了是否是唯一解。模板传送门
#include<bits/stdc++.h>
using namespace std;
const int N=105;
const double eps=1e-8;
double a[N][N],ans[N];int n;
inline int sgn(double x){
if(x>eps) return 1;
if(x<-eps) return -1;
return 0;
}
inline int Guass(){
for(int i=1;i<=n;++i){
int k=i;
for(int j=i+1;j<=n;++j)
if(fabs(a[k][i])<fabs(a[j][i])) k=j;
swap(a[i],a[k]);
if(!sgn(a[i][i])) return 0;
for(int j=i+1;j<=n;++j)
for(int k=i+1;k<=n+1;++k)
a[j][k]-=a[j][i]/a[i][i]*a[i][k];
}
for(int i=n;i;--i){
for(int j=i+1;j<=n;++j)
a[i][n+1]-=ans[j]*a[i][j];
ans[i]=a[i][n+1]/a[i][i];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n+1;++j)
scanf("%lf",&a[i][j]);
if(!Guass()) return puts("No Solution"),0;
for(int i=1;i<=n;++i) printf("%.2lf\n",ans[i]);
}
欧拉定理&广义欧拉定理
欧拉定理:若
a
,
m
a,m
a,m为正整数,且
g
c
d
(
a
,
m
)
=
1
gcd(a,m)=1
gcd(a,m)=1,那么有:
a
φ
(
m
)
≡
1
(
m
o
d
m
)
a^{\varphi(m)} \equiv 1 \ (mod \ m)
aφ(m)≡1 (mod m)
广义欧拉定理:
a
b
≡
{
a
b
%
φ
(
m
)
,
g
c
d
(
a
,
m
)
=
1
a
b
,
g
c
d
(
a
,
m
)
≠
1
,
b
<
φ
(
m
)
a
b
%
φ
(
m
)
+
φ
(
m
)
,
g
c
d
(
a
,
m
)
≠
1
,
b
⩾
φ
(
m
)
a^b\equiv \begin{cases} a^{b\%\varphi(m)},gcd(a,m)=1\\ a^{b},gcd(a,m)\not=1,b<\varphi(m)\\ a^{b\%\varphi(m)+\varphi(m)},gcd(a,m)\not=1,b \geqslant \varphi(m) \end{cases}
ab≡⎩⎪⎨⎪⎧ab%φ(m),gcd(a,m)=1ab,gcd(a,m)=1,b<φ(m)ab%φ(m)+φ(m),gcd(a,m)=1,b⩾φ(m)
在快速幂的时候要仔细想一想指数对不对,毕竟指数不能直接用
m
m
m取模。
原根
参考文章
参考《初等数论》
P
241
P241
P241
定义1:
m
⩾
1
,
(
a
,
m
)
=
1
m \geqslant 1,(a,m)=1
m⩾1,(a,m)=1,使:
a
d
≡
1
(
m
o
d
m
)
a^d \equiv 1\ (mod\ m)
ad≡1 (mod m)成立的最小正整数
d
d
d称为a对模m的指数(或阶),把它记做
δ
m
(
a
)
\delta_m(a)
δm(a).
定义2:当
δ
m
(
a
)
=
φ
(
m
)
\delta_m(a)=\varphi(m)
δm(a)=φ(m)时,称
a
a
a是模
m
m
m的原根,简称m的原根。
由欧拉定理:
a
φ
(
m
)
≡
1
(
m
o
d
m
)
a^{\varphi(m)} \equiv 1 \ (mod \ m)
aφ(m)≡1 (mod m),可知
δ
m
(
a
)
⩽
φ
(
m
)
\delta_m(a) \leqslant \varphi(m)
δm(a)⩽φ(m)
如何求质数的原根?
一般来说原根不会太大,暴力枚举
[
2
,
⌊
x
4
⌋
]
[2,\lfloor \sqrt[4] x \rfloor]
[2,⌊4x⌋]即可。
如何验证当前枚举的
a
a
a是不是原根?
首先,循环节长度
s
s
s一定满足
s
∣
φ
(
m
)
s|\varphi(m)
s∣φ(m)。
那么只需要找出
φ
(
m
)
\varphi(m)
φ(m)的所有质因数
p
1
,
p
2
,
⋯
,
p
k
p_1,p_2,\cdots,p_k
p1,p2,⋯,pk。
如果:
∀
p
i
,
i
∈
[
1
,
k
]
:
a
φ
(
m
)
p
i
≢
1
(
m
o
d
m
)
\forall p_i,i\in[1,k]:a^{\frac{\varphi(m)}{p_i}} \not\equiv1\ (mod\ m)
∀pi,i∈[1,k]:apiφ(m)≡1 (mod m)
则
a
a
a是
m
m
m的一个原根。附上模板
#include<bits/stdc++.h>
using namespace std;
int mod,tot=0,factor[31];
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int quickpow(int a,int b,int ret=1){for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
inline bool is_pri_root(int x){
for(int i=1;i<=tot;++i)
if(quickpow(x,(mod-1)/factor[i])==1)
return false;
return true;
}
inline int find(int mod){
int phi=mod-1;
for(int i=2;i*i<=phi;++i)
while(phi%i==0) factor[++tot]=i,phi/=i;
if(phi!=1) factor[++tot]=phi;
for(int i=1;;++i)
if(is_pri_root(i)) return i;
}
int main(){
scanf("%d",&mod),printf("%d\n",find(mod));
}
二项式反演
神犇666
f
n
=
∑
i
=
0
n
(
−
1
)
i
(
n
i
)
g
i
⟺
g
n
=
∑
i
=
0
n
(
−
1
)
i
(
n
i
)
f
i
f_n=\sum_{i=0}^{n}(-1)^i \binom{n}{i}g_i \Longleftrightarrow g_n=\sum_{i=0}^{n}(-1)^i \binom{n}{i}f_i
fn=i=0∑n(−1)i(in)gi⟺gn=i=0∑n(−1)i(in)fi
这个式子有很强的对称性。一个更常用的结论:
f
n
=
∑
i
=
0
n
(
n
i
)
g
i
⟺
g
n
=
∑
i
=
0
n
(
−
1
)
n
−
i
(
n
i
)
f
i
f_n=\sum_{i=0}^{n} \binom{n}{i}g_i \Longleftrightarrow g_n=\sum_{i=0}^{n}(-1)^{n-i} \binom{n}{i}f_i
fn=i=0∑n(in)gi⟺gn=i=0∑n(−1)n−i(in)fi
可以用来解决
m
i
n
_
m
a
x
min\_max
min_max容斥。
错排问题
例题
a
a
a数组为
1
1
1~
n
n
n的排列,
∀
a
[
i
]
!
=
i
\forall a[i]!=i
∀a[i]!=i的排列方法有多少种?
令
D
[
n
]
D[n]
D[n]为长度为
n
n
n的排列的错排方案数,有递推公式:
D
[
n
]
=
(
n
−
1
)
∗
(
D
[
n
−
1
]
+
D
[
n
−
2
]
)
D[n]=(n-1)*(D[n-1]+D[n-2])
D[n]=(n−1)∗(D[n−1]+D[n−2])
证明:
考虑增量法求
D
[
n
]
D[n]
D[n],假设已知
D
[
1
]
D[1]
D[1]~
D
[
n
−
1
]
D[n-1]
D[n−1]:
现在令所有元素有序排列好,即
a
[
i
]
=
i
a[i]=i
a[i]=i。
第
n
n
n个元素可以选择前
n
−
1
n-1
n−1个位置中的任意一个放进去,假设选了某个位置
k
k
k,那么这个位置的元素
k
k
k就被挤出来了。
如果
k
k
k放在了位置
n
n
n,现在就相当于是
n
n
n和
k
k
k交换了位置,剩下
n
−
2
n-2
n−2个元素需要错排。即为
D
[
n
−
2
]
D[n-2]
D[n−2]种方案。
如果
k
k
k不放在位置
n
n
n,相当于是把
a
[
k
]
!
=
k
a[k]!=k
a[k]!=k的限制变为了
a
[
n
]
!
=
k
a[n]!=k
a[n]!=k,由于
n
n
n的位置已经固定在
k
k
k,所以现在问题转化为了
1
1
1~
n
−
1
n-1
n−1的错排问题,方案数为
D
[
n
−
1
]
D[n-1]
D[n−1]。这
n
−
1
n-1
n−1个限制关系依然是一一对应的。
于是有: D [ n ] = ( n − 1 ) ∗ ( D [ n − 1 ] + D [ n − 2 ] ) D[n]=(n-1)*(D[n-1]+D[n-2]) D[n]=(n−1)∗(D[n−1]+D[n−2])
初始状态 D [ 1 ] = 0 , D [ 2 ] = 1 D[1]=0,D[2]=1 D[1]=0,D[2]=1
二次剩余
以下船部内容参考:
《初等数论》第四章§5:模为素数的二次剩余
假定
p
p
p是奇素数,设
p
̸
∣
a
p \not\;\mid a
p∣a,二次同余方程的一般形式是:
a
x
2
+
b
x
+
c
≡
0
(
m
o
d
p
)
⋯
(
1
)
ax^2+bx+c \equiv 0\ (mod\ p) \cdots(1)
ax2+bx+c≡0 (mod p)⋯(1)
由于
p
p
p为奇素数,
p
̸
∣
a
p \not\;\mid a
p∣a,所以
p
̸
∣
4
a
p \not\;\mid 4a
p∣4a,所以
(
1
)
(1)
(1)式和同余方程:
4
a
(
a
x
2
+
b
x
+
c
)
≡
0
(
m
o
d
p
)
4a(ax^2+bx+c) \equiv 0\ (mod\ p)
4a(ax2+bx+c)≡0 (mod p)
的解相同。上式可写为:
(
2
a
x
+
b
)
2
≡
b
2
−
4
a
c
(
m
o
d
p
)
(2ax+b)^2 \equiv b^2-4ac\ (mod\ p)
(2ax+b)2≡b2−4ac (mod p)
令
y
=
2
a
x
+
b
y=2ax+b
y=2ax+b,于是有:
y
≡
2
a
x
+
b
(
m
o
d
p
)
y \equiv 2ax+b\ (mod\ p)
y≡2ax+b (mod p),且:
y
2
≡
b
2
−
4
a
c
(
m
o
d
p
)
y^2 \equiv b^2-4ac\ (mod\ p)
y2≡b2−4ac (mod p)
显然
x
x
x和
y
y
y是一一对应的。于是问题转化为形如:
x
2
≡
d
(
m
o
d
p
)
x^2 \equiv d\ (mod\ p)
x2≡d (mod p)
的同余方程,也就是求解
y
y
y。当
p
∣
d
p \mid d
p∣d,仅有一解:
x
≡
0
(
m
o
d
p
)
x\equiv0\ (mod\ p)
x≡0 (mod p)。
所以,下面恒假定
p
̸
∣
d
p \not\;\mid d
p∣d。
定义1:设素数
p
>
2
,
d
∈
Z
,
p
̸
∣
d
p>2,d\in \Z,p \not\;\mid d
p>2,d∈Z,p∣d,若
x
2
≡
d
(
m
o
d
p
)
x^2 \equiv d\ (mod\ p)
x2≡d (mod p)有解,称d是模p的二次剩余,否则称d是模p的二次非剩余
定理1:在模
p
p
p的一个既约剩余系中,恰有
p
−
1
2
\frac{p-1}{2}
2p−1个模
p
p
p的二次剩余,
p
−
1
2
\frac{p-1}{2}
2p−1个模
p
p
p的二次非剩余。若
d
d
d是模
p
p
p的二次剩余,则
x
2
≡
d
(
m
o
d
p
)
x^2 \equiv d\ (mod\ p)
x2≡d (mod p)的解数为
2
2
2。
证明略。
定理2(Euler判别法):设素数
p
>
2
p>2
p>2,
p
̸
∣
d
p \not\;\mid d
p∣d,那么:
d
d
d是模
p
p
p的二次剩余的充分必要条件是:
d
p
−
1
2
≡
1
(
m
o
d
p
)
⋯
(
3
)
d^{\frac{p-1}{2}}\equiv 1\ (mod\ p)\cdots(3)
d2p−1≡1 (mod p)⋯(3)
d
d
d是模
p
p
p的非二次剩余的充分必要条件是:
d
p
−
1
2
≡
−
1
(
m
o
d
p
)
⋯
(
4
)
d^{\frac{p-1}{2}}\equiv -1\ (mod\ p)\cdots(4)
d2p−1≡−1 (mod p)⋯(4)
Wilson定理:设
p
p
p是素数,
r
1
,
⋯
,
r
p
−
1
r_1,\cdots,r_{p-1}
r1,⋯,rp−1是模
p
p
p的既约剩余系,有:
r
1
⋯
r
p
−
1
≡
−
1
(
m
o
d
p
)
r_1\cdots r_{p-1}\equiv -1(mod\ p)
r1⋯rp−1≡−1(mod p)
特别地,有:
(
p
−
1
)
!
≡
−
1
(
m
o
d
p
)
(p-1)!\equiv\ -1(mod\ p)
(p−1)!≡ −1(mod p)
下面证明
E
u
l
e
r
Euler
Euler判别法:
首先证明
∀
d
∈
Z
,
p
̸
∣
d
\forall d\in \Z,p\not\;\mid d
∀d∈Z,p∣d,
(
3
)
(3)
(3)或
(
4
)
(4)
(4)有且仅有一式成立(平方差):
d
p
−
1
≡
1
(
m
o
d
p
)
⇒
(
d
p
−
1
2
−
1
)
(
d
p
−
1
2
+
1
)
≡
0
(
m
o
d
p
)
d^{p-1}\equiv 1(mod\ p) \Rightarrow (d^{\frac{p-1}{2}}-1)(d^{\frac{p-1}{2}}+1)\equiv 0 \ (mod\ p)
dp−1≡1(mod p)⇒(d2p−1−1)(d2p−1+1)≡0 (mod p)
所以不可能存在
(
3
)
(3)
(3)、
(
4
)
(4)
(4)都不成立的情况。
若两式都成立,则
p
∣
(
d
p
−
1
2
−
1
)
,
p
∣
(
d
p
−
1
2
+
1
)
p \mid (d^{\frac{p-1}{2}}-1),p\mid (d^{\frac{p-1}{2}}+1)
p∣(d2p−1−1),p∣(d2p−1+1)
⇒
p
∣
(
d
p
−
1
2
+
1
)
−
(
d
p
−
1
2
−
1
)
⇒
p
∣
2
\Rightarrow p \mid (d^{\frac{p-1}{2}}+1)-(d^{\frac{p-1}{2}}-1)\Rightarrow p \mid 2
⇒p∣(d2p−1+1)−(d2p−1−1)⇒p∣2
由于
p
p
p为奇素数,所以上述情况不可能成立,矛盾。
所以
(
3
)
(3)
(3)或
(
4
)
(4)
(4)有且仅有一式成立。
(
3
)
(3)
(3)的必要性:若
d
d
d是模
p
p
p的二次剩余,则有
x
0
x_0
x0使得:
x
0
2
≡
d
(
m
o
d
p
)
⇒
x
0
p
−
1
≡
d
p
−
1
2
(
m
o
d
p
)
⇒
1
≡
d
p
−
1
2
(
m
o
d
p
)
x_0^2\equiv d(mod\ p) \Rightarrow x_0^{p-1}\equiv d^{\frac{p-1}{2}}(mod\ p) \Rightarrow 1\equiv d^{\frac{p-1}{2}}(mod\ p)
x02≡d(mod p)⇒x0p−1≡d2p−1(mod p)⇒1≡d2p−1(mod p)
(
3
)
(3)
(3)的充分性:若
(
3
)
(3)
(3)成立,则必有
p
̸
∣
d
p\not\;\mid d
p∣d.考虑一次同余方程:
a
x
≡
d
(
m
o
d
p
)
ax\equiv d(mod\ p)
ax≡d(mod p)
令模
p
p
p的既约剩余系为
S
S
S。那么对于
∀
j
∈
S
\forall j\in S
∀j∈S,当
a
=
j
a=j
a=j,必有唯一的
x
=
x
j
∈
S
x=x_j\in S
x=xj∈S使得方程成立。假设
d
d
d不是模
p
p
p的二次剩余:
那么必有
j
≠
x
j
j\not=x_j
j=xj,否则若
j
=
x
j
j=x_j
j=xj,有
j
∗
x
j
≡
d
≡
x
j
2
(
m
o
d
p
)
j*x_j \equiv d \equiv x_j^2 (mod\ p)
j∗xj≡d≡xj2(mod p),推出
d
d
d是模
p
p
p的二次剩余,矛盾。
现在把
S
S
S中的数按照
j
,
x
j
j,x_j
j,xj两两配对,可以配出
p
−
1
2
\frac{p-1}{2}
2p−1组:
j
∗
x
j
≡
d
(
m
o
d
p
)
j*x_j \equiv d (mod\ p)
j∗xj≡d(mod p)
于是有:
(
p
−
1
)
!
≡
d
p
−
1
2
(
m
o
d
p
)
(p-1)!\equiv d^{\frac{p-1}{2}} (mod\ p)
(p−1)!≡d2p−1(mod p)
由
W
i
l
s
o
n
Wilson
Wilson定理:
−
1
≡
d
p
−
1
2
(
m
o
d
p
)
-1\equiv d^{\frac{p-1}{2}}(mod\ p)
−1≡d2p−1(mod p)
与式
(
3
)
(3)
(3)矛盾。所以必有
j
0
j_0
j0使得
j
0
=
x
j
0
j_0=x_{j_0}
j0=xj0,所以
d
d
d是模
p
p
p的二次剩余。这就证明了充分性。由已证部分可以立即推出剩下的结论。
Cipolla算法
求解关于x的同余方程:
x
2
≡
n
(
m
o
d
p
)
x^2\equiv n(mod\ p)
x2≡n(mod p)
p
p
p为奇素数。算法流程:
①首先找到
a
a
a使得
a
2
−
n
a^2-n
a2−n为模
p
p
p的二次非剩余。根据上面的定理1,在模
p
p
p的一个既约剩余系中,恰有
p
−
1
2
\frac{p-1}{2}
2p−1个模
p
p
p的二次剩余,所以如果随机取这个
a
a
a,找到满足条件的
a
a
a的期望次数为
2
2
2。根据
E
u
l
e
r
Euler
Euler判别法,
(
a
2
−
n
)
p
−
1
2
≡
−
1
(
m
o
d
p
)
{(a^2-n)}^{\frac{p-1}{2}}\equiv -1(mod\ p)
(a2−n)2p−1≡−1(mod p)时满足条件。
②令
ω
=
a
2
−
n
\omega=\sqrt{a^2-n}
ω=a2−n,求出
x
0
=
(
a
+
ω
)
p
+
1
2
x_0=(a+\omega)^{\frac{p+1}{2}}
x0=(a+ω)2p+1,答案有两个:
x
1
=
x
0
,
x
2
=
p
−
x
0
x1=x_0,x_2=p-x_0
x1=x0,x2=p−x0。
几个需要特判的东西:
①
n
=
0
n=0
n=0,则解为
x
1
=
x
2
=
0
x_1=x_2=0
x1=x2=0。
②
p
=
2
p=2
p=2,则返回
n
n
n即可(
n
n
n只有
0
,
1
0,1
0,1两种取值,两个解相同)
其他情况都有两个解。
洛谷模板现在写代码越写越丑了。。
#include<bits/stdc++.h>
#define re register
#define cs const
namespace IO{
cs int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
template <typename T>
inline T get(){
char ch=gc();T x=0;
while(!isdigit(ch)) ch=gc();
while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
return x;
}
inline int gi(){return get<int>();}
}
using IO::gi;
int T,n,mod,NQ;
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int quickpow(int a,int b,int ret=1){for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
struct plx{
int x,y;
plx(int X=0,int Y=0){x=X,y=Y;}
friend inline plx operator*(cs plx &a,cs plx &b){
return plx(add(mul(a.x,b.x),mul(mul(a.y,b.y),dec(mul(NQ,NQ),n))),add(mul(a.x,b.y),mul(a.y,b.x)));
}
friend inline plx operator^(plx a,int b){
plx ret(1,0);
for(;b;b>>=1,a=a*a) if(b&1)
ret=ret*a;
return ret;
}
};
inline void solve(){
n%=mod;
if(!n){puts("0");return;}
if(mod==2){printf("%d\n",n);return;}
if(quickpow(n,(mod-1)>>1)==mod-1){puts("Hola!");return;}
while(1){
NQ=rand()%mod;
if(quickpow(dec(mul(NQ,NQ),n),(mod-1)>>1)==mod-1)
break;
}
int x0=(plx(NQ,1)^((mod+1)>>1)).x,x1=mod-x0;
if(x0>x1) std::swap(x0,x1);
printf("%d %d\n",x0,x1);
}
int main(){
// freopen("5491.in","r",stdin);
T=gi();
while(T--) n=gi(),mod=gi(),solve();
}
积性函数的卷积
I
∗
μ
=
ϵ
I*\mu=\epsilon
I∗μ=ϵ
μ
∗
i
d
=
φ
\mu * id=\varphi
μ∗id=φ
I
∗
i
d
=
σ
I*id=\sigma
I∗id=σ
I
∗
I
=
d
I*I=d
I∗I=d
I
∗
φ
=
i
d
I*\varphi=id
I∗φ=id
注意
i
d
(
x
)
=
x
,
I
(
x
)
≡
1
id(x)=x,I(x) \equiv1
id(x)=x,I(x)≡1,不要搞混了T_T
1
1
1~
n
n
n中与
n
n
n互质的数的和:
∑
i
=
1
n
[
g
c
d
(
n
,
i
)
=
=
1
]
∗
i
=
n
∗
φ
(
n
)
+
[
n
=
=
1
]
2
\sum_{i=1}^{n} [gcd(n,i)==1]*i=\frac{n*\varphi(n)+[n==1]}{2}
i=1∑n[gcd(n,i)==1]∗i=2n∗φ(n)+[n==1]
当
n
=
1
n=1
n=1或
n
=
2
n=2
n=2时,上式和即为1.
当
n
>
2
n>2
n>2时,
φ
(
n
)
\varphi(n)
φ(n)为偶数。如果
g
c
d
(
n
,
a
)
=
1
gcd(n,a)=1
gcd(n,a)=1,则
g
c
d
(
n
,
n
−
a
)
=
1
gcd(n,n-a)=1
gcd(n,n−a)=1
也就是说这些与
n
n
n互质的数两两配对凑出了
n
n
n,于是就有了上式。