数论学习笔记
数论学习笔记
整除
a ∣ b a|b a∣b: ∃ q ∈ Z , b = a × q \exists q\in \mathbb{Z},b=a\times q ∃q∈Z,b=a×q 则称 b b b 是 a a a 的倍数, a a a 是 b b b 的约数
基本性质:
- 传递性:如果 a ∣ b a|b a∣b 且 b ∣ c b|c b∣c ,那么 a ∣ c a|c a∣c
- 同乘性:若 m ≠ 0 m\neq0 m=0, a ∣ b ⇔ ( m × a ) ∣ ( m × b ) a|b \Leftrightarrow (m\times a)|(m\times b) a∣b⇔(m×a)∣(m×b)
- a ∣ b a|b a∣b 且 a ∣ c a|c a∣c 等价于 ∀ x , y ∈ Z , a ∣ ( b × x + c × y ) \forall x,y\in \mathbb{Z},a|(b\times x+c\times y) ∀x,y∈Z,a∣(b×x+c×y)
- 若
x
,
y
∈
Z
,
a
x
+
b
y
=
1
x,y\in\mathbb{Z},ax+by=1
x,y∈Z,ax+by=1,且
a
∣
n
a|n
a∣n,
b
∣
n
b|n
b∣n 则
(
a
×
b
)
∣
n
(a\times b)|n
(a×b)∣n(由2,3可推)
证明:由性质3得 ( a × b ) ∣ ( b × n ) (a\times b)|(b\times n) (a×b)∣(b×n) 且 ( a × b ) ∣ ( a × n ) (a\times b)|(a\times n) (a×b)∣(a×n)。又由性质2得 ( a × b ) ∣ ( a × n × x + b × n × y ) = ( a × b ) ∣ n (a\times b)|(a\times n\times x+b\times n\times y)=(a\times b)|n (a×b)∣(a×n×x+b×n×y)=(a×b)∣n - 若 b = q d + c b=qd+c b=qd+c,那么 d ∣ b d|b d∣b 的充要条件是 d ∣ c d|c d∣c
同余
若 a , b a,b a,b 为两个整数,且它们的差 a − b a-b a−b 能被某个自然数 m m m 所整除,则 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm) ,意味着 a − b = m k , k ∈ Z a-b=mk,k\in\mathbb{Z} a−b=mk,k∈Z
基本性质:
- 自反性: a ≡ a ( m o d m ) a\equiv a \pmod m a≡a(modm)
- 对称性:若 a ≡ b ( m o d m ) a\equiv b \pmod m a≡b(modm),则 b ≡ a ( m o d m ) b\equiv a \pmod m b≡a(modm)
- 传递性:若 a ≡ b ( m o d m ) , b ≡ c ( m o d m ) a\equiv b \pmod m,b\equiv c \pmod m a≡b(modm),b≡c(modm),则 a ≡ c ( m o d m ) a\equiv c \pmod m a≡c(modm)
- 同加性:若 a ≡ b ( m o d m ) a\equiv b \pmod m a≡b(modm),则 a + c ≡ b + c ( m o d m ) a+c\equiv b+c \pmod m a+c≡b+c(modm)
- 同乘性:若
a
≡
b
(
m
o
d
m
)
a\equiv b \pmod m
a≡b(modm),则
a
c
≡
b
c
(
m
o
d
m
)
ac\equiv bc \pmod m
ac≡bc(modm);
若 a ≡ b ( m o d m ) , c ≡ d ( m o d m ) a\equiv b \pmod m,c\equiv d \pmod m a≡b(modm),c≡d(modm),则 a c ≡ b d ( m o d m ) ac\equiv bd \pmod m ac≡bd(modm) - 同幂性:若 a ≡ b ( m o d m ) a\equiv b \pmod m a≡b(modm),则 a n ≡ b n ( m o d m ) a^n\equiv b^n \pmod m an≡bn(modm)
最大公约数
本节详解见:https://oi-wiki.org/math/gcd/
辗转相除法,又称欧几里得算法。一次有效的取模后,
n
n
n 至少减少一半 , 所以复杂度
O
(
log
n
)
O(\log n)
O(logn)
递归式
gcd
(
x
,
y
)
=
gcd
(
y
,
x
m
o
d
y
)
\gcd(x,y) = \gcd(y,x\bmod y)
gcd(x,y)=gcd(y,xmody)
int gcd(int x,int y)//使用三目运算符
{
return y==0?x:gcd(y,x%y);
}
int gcd(int x,int y)//普通递归求解
{
if(y == 0) return x;
return gcd(y,x%y);
}
定理:
公约数一定是最大公约数的约数
lcm ( x , y ) = x y gcd ( x , y ) \operatorname{lcm}(x,y)=\dfrac{xy}{\gcd(x,y)} lcm(x,y)=gcd(x,y)xy
定理:
公倍数一定是最小公倍数的倍数
注意:
gcd ( 0 , n ) = n \gcd(0,n) = n gcd(0,n)=n
裴蜀定理
对于正整数 a , b a,b a,b 和 d d d , gcd ( a , b ) ∣ d ⇔ ∃ x , y ∈ Z , a x + b y = d \gcd(a,b) | d \Leftrightarrow \exist x,y\in \mathbb{Z},ax+by=d gcd(a,b)∣d⇔∃x,y∈Z,ax+by=d
类欧几里得算法
f
(
a
,
b
,
c
,
n
)
=
∑
⌊
a
i
+
b
c
⌋
f(a,b,c,n)=\sum \lfloor\dfrac{ai+b}{c}\rfloor
f(a,b,c,n)=∑⌊cai+b⌋
问直线
y
=
a
c
x
+
b
c
y=\dfrac{a}{c}x+\dfrac{b}{c}
y=cax+cb 与
x
x
x 轴围成的区域内整点的数量
扩展欧几里得算法
常用于求
a
x
+
b
y
=
gcd
(
a
,
b
)
ax+by=\gcd(a,b)
ax+by=gcd(a,b) 的一组可行解。
由裴蜀定理可以知道,
a
x
+
b
y
=
gcd
(
a
,
b
)
ax+by=\gcd(a,b)
ax+by=gcd(a,b) 一定有解
求解思路:
设
a
x
1
+
b
y
1
=
gcd
(
a
,
b
)
,
b
x
2
+
(
a
m
o
d
b
)
y
2
=
gcd
(
b
,
a
m
o
d
b
)
ax_1+by_1=\gcd(a,b),bx_2+(a \bmod b)y_2=\gcd(b,a \bmod b)
ax1+by1=gcd(a,b),bx2+(amodb)y2=gcd(b,amodb)(根据最大公约数的特征列两条等式)
∵
gcd
(
a
,
b
)
=
gcd
(
b
,
a
m
o
d
b
)
\because \gcd(a,b) = \gcd(b,a\bmod b)
∵gcd(a,b)=gcd(b,amodb)
∴
a
x
1
+
b
y
1
=
b
x
2
+
(
a
m
o
d
b
)
y
2
\therefore ax_1+by_1=bx_2+(a\bmod b)y_2
∴ax1+by1=bx2+(amodb)y2
∵
a
m
o
d
b
=
a
−
(
⌊
a
b
⌋
×
b
)
\because a \bmod b = a-(\lfloor\dfrac{a}{b}\rfloor\times b)
∵amodb=a−(⌊ba⌋×b)
∴
a
x
1
+
b
y
1
=
b
x
2
+
[
a
−
(
⌊
a
b
⌋
)
×
b
]
y
2
a
x
1
+
b
y
1
=
a
y
2
+
b
[
x
2
−
(
⌊
a
b
⌋
)
y
2
]
\begin{aligned}\therefore ax_1+by_1 & = bx_2+[a-(\lfloor\dfrac{a}{b}\rfloor)\times b]y_2\\ax_1+by_1 & = ay_2+b[x_2-(\lfloor\dfrac{a}{b}\rfloor)y_2]\end{aligned}
∴ax1+by1ax1+by1=bx2+[a−(⌊ba⌋)×b]y2=ay2+b[x2−(⌊ba⌋)y2]
∴
x
1
=
y
2
,
y
1
=
x
2
−
⌊
a
b
⌋
y
2
\therefore x_1 = y_2,y_1 = x_2-\lfloor\dfrac{a}{b}\rfloor y_2
∴x1=y2,y1=x2−⌊ba⌋y2(通过推导发现两等式的递归关系)
求出的解满足 ∣ x ∣ + ∣ y ∣ |x|+|y| ∣x∣+∣y∣ 最小。
由此可以递归代入求解:
- 递归求 gcd \gcd gcd
- 当 b = 0 b=0 b=0 时, a x + b y = a x = a ax+by=ax=a ax+by=ax=a,解得 x = 1 x=1 x=1,递归回代
- 求过 gcd \gcd gcd 后,求的当前解 x 1 = y 2 , y 1 = x 2 − ⌊ a b ⌋ y 2 x_1 = y_2,y_1 = x_2-\lfloor\dfrac{a}{b}\rfloor y_2 x1=y2,y1=x2−⌊ba⌋y2
int exgcd(int a,int b,int &x,int &y)
{
if(b == 0)
{
x = 1;//特殊解
y = 0;
return a;//返回gcd
}
int gcd = exgcd(b,a%b,x,y);//先求gcd
//根据公式回代求解
int x2 = x;int y2 = y;
int x1 = y2;int y1 = x2-(a/b)*y2;
x = x1;y = y1;
return gcd;
}
求解线性同余方程
形如
a
x
≡
c
(
m
o
d
b
)
ax\equiv c\pmod b
ax≡c(modb) 的方程称作线性同余方程。
解线性同余方程其实就是解二元一次不定方程。
定理1:
方程 a x + b y = c ax+by=c ax+by=c 等价于 a x ≡ c ( m o d b ) ax\equiv c\pmod b ax≡c(modb);有整数解的充要条件是: gcd ( a , b ) ∣ c \gcd(a,b)|c gcd(a,b)∣c
定理2:
若 gcd ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1,且 x 0 , y 0 x_0,y_0 x0,y0 为 a x + b y = c ax+by=c ax+by=c 的一组解,则该方程的任意解可表示为: x = x 0 + b t , y = y 0 − a t x=x_0+bt,y=y_0-at x=x0+bt,y=y0−at,且对任意整数 t t t 都成立。
求一组特殊解:
由定理1得:
a
x
0
+
b
y
0
=
gcd
(
a
,
b
)
x
′
=
x
0
×
c
gcd
(
a
,
b
)
,
y
′
=
y
0
×
c
gcd
(
a
,
b
)
ax_0+by_0=\gcd(a,b)\\ x'=x_0\times\dfrac{c}{\gcd(a,b)},y'=y_0\times\dfrac{c}{\gcd(a,b)}
ax0+by0=gcd(a,b)x′=x0×gcd(a,b)c,y′=y0×gcd(a,b)c
这里的
x
′
,
y
′
x',y'
x′,y′ 是一组特殊解。
线性同余方程的最小正整数解:
x
=
(
x
m
o
d
t
+
t
)
m
o
d
t
,
t
=
b
gcd
(
a
,
b
)
x=(x \bmod t+t)\bmod t,t=\dfrac{b}{\gcd(a,b)}
x=(xmodt+t)modt,t=gcd(a,b)b
模板题:P1082 [NOIP2012 提高组] 同余方程
P1516 青蛙的约会
根据题意,设二者跳 t t t 次相遇,有方程 ( x + t × m ) m o d L = ( y + t × n ) m o d L (x+t\times m)\bmod L=(y+t\times n)\bmod L (x+t×m)modL=(y+t×n)modL ,将其转为同余方程的形式得到 ( x + t × m ) ≡ ( y + t × n ) ( m o d L ) (x+t\times m)\equiv (y+t\times n) \pmod L (x+t×m)≡(y+t×n)(modL) ,通过恒等变换得到 ( m − n ) t ≡ ( y − x ) ( m o d L ) (m-n)t\equiv (y-x)\pmod L (m−n)t≡(y−x)(modL),再转化为二元一次不定方程 ( m − n ) × t + L × k = ( y − x ) (m-n)\times t +L\times k=(y-x) (m−n)×t+L×k=(y−x),用扩展欧几里得解此方程 t t t 的最小正整数解即可,注意正负这类小细节的判断。
#include<iostream>
#include<cmath>
using namespace std;
long long x,y,m,n,L;
long long a,b,ansx,ansy,c,d;
void exgcd(long long &x,long long &y,long long a,long long b,long long &c)
{
if(b==0)
{
c=a;x=1;y=0;
return ;
}
exgcd(x,y,b,a%b,c);
long long tmp=x;
x=y;
y=tmp-(a/b)*y;
return ;
}
int main()
{
cin>>x>>y>>m>>n>>L;
if(n<m)
{
swap(m,n);
swap(x,y);
}
a=n-m;b=L;c=x-y;
exgcd(ansx,ansy,a,b,d);
if(c%d!=0||m==n)
{
cout<<"Impossible";
return 0;
}
cout<<(ansx*c/d%(L/d)+(L/d))%(L/d);
return 0;
}
P2054 [AHOI2005]洗牌
P2054 [AHOI2005]洗牌
观察数字
i
i
i 在进行
k
k
k 次洗牌后的位置,发现它的位置将在
2
k
×
i
m
o
d
(
n
+
1
)
2^k\times i\bmod (n+1)
2k×imod(n+1)
所以得到方程
2
m
×
i
m
o
d
(
n
+
1
)
=
L
⇔
2
m
×
i
m
o
d
(
n
+
1
)
=
L
m
o
d
(
n
+
1
)
⇔
2
m
×
i
≡
L
(
m
o
d
n
+
1
)
⇔
(
2
m
m
o
d
(
n
+
1
)
)
×
i
≡
L
(
m
o
d
n
+
1
)
\begin{aligned}2^m\times i\bmod (n+1)=L &\Leftrightarrow2^m\times i\bmod (n+1)=L\bmod (n+1)\\&\Leftrightarrow 2^m\times i\equiv L&\pmod{n+1}\\&\Leftrightarrow(2^m\bmod(n+1))\times i\equiv L &\pmod{n+1}\end{aligned}
2m×imod(n+1)=L⇔2m×imod(n+1)=Lmod(n+1)⇔2m×i≡L⇔(2mmod(n+1))×i≡L(modn+1)(modn+1)
然后解
i
i
i 就好了
二元一次不定方程
一般形式
a
x
+
b
y
=
c
ax+by=c
ax+by=c
方程所有整数解可表示为:
x
=
x
′
+
b
gcd
(
a
,
b
)
t
y
=
y
′
−
a
gcd
(
a
,
b
)
t
x=x'+\dfrac{b}{\gcd(a,b)}t\\[2ex] y=y'-\dfrac{a}{\gcd(a,b)}t
x=x′+gcd(a,b)bty=y′−gcd(a,b)at
其中
t
∈
Z
t\in \mathbb{Z}
t∈Z,
x
′
,
y
′
x',y'
x′,y′ 是一组特殊解
a
x
0
+
b
y
0
=
gcd
(
a
,
b
)
x
′
=
x
0
×
c
gcd
(
a
,
b
)
,
y
′
=
y
0
×
c
gcd
(
a
,
b
)
ax_0+by_0=\gcd(a,b)\\ x'=x_0\times\dfrac{c}{\gcd(a,b)},y'=y_0\times\dfrac{c}{\gcd(a,b)}
ax0+by0=gcd(a,b)x′=x0×gcd(a,b)c,y′=y0×gcd(a,b)c
P5656 【模板】二元一次不定方程 (exgcd)
判断有无整数解只需要看是否满足
gcd
(
a
,
b
)
∣
c
\gcd(a,b)|c
gcd(a,b)∣c 即可
接下来考虑是否存在正整数解的问题:
如果存在正整数解,那么
{
x
=
x
′
+
b
gcd
(
a
,
b
)
t
>
0
y
=
y
′
−
a
gcd
(
a
,
b
)
t
>
0
\begin{cases} x=x'+\dfrac{b}{\gcd(a,b)}t >0\\[2ex] y=y'-\dfrac{a}{\gcd(a,b)}t >0 \end{cases}
⎩⎪⎪⎨⎪⎪⎧x=x′+gcd(a,b)bt>0y=y′−gcd(a,b)at>0
解得:
−
x
′
×
gcd
(
a
,
b
)
b
<
t
<
y
′
×
gcd
(
a
,
b
)
a
\dfrac{-x'\times\gcd(a,b)}{b}<t<\dfrac{y'\times\gcd(a,b)}{a}
b−x′×gcd(a,b)<t<ay′×gcd(a,b)
又因为
t
∈
Z
t\in\mathbb{Z}
t∈Z ,所以
⌈
−
x
′
×
gcd
(
a
,
b
)
b
⌉
⩽
t
⩽
⌊
y
′
×
gcd
(
a
,
b
)
a
⌋
\lceil\dfrac{-x'\times\gcd(a,b)}{b}\rceil\leqslant t\leqslant \lfloor\dfrac{y'\times\gcd(a,b)}{a}\rfloor
⌈b−x′×gcd(a,b)⌉⩽t⩽⌊ay′×gcd(a,b)⌋
如果存在
t
t
t 满足上述不等式,则方程有正整数解,否则没有。
如果有整数解:
显然整数解个数
n
=
⌊
y
′
×
gcd
(
a
,
b
)
a
⌋
−
⌈
−
x
′
×
gcd
(
a
,
b
)
b
⌉
n=\lfloor\dfrac{y'\times\gcd(a,b)}{a}\rfloor-\lceil\dfrac{-x'\times\gcd(a,b)}{b}\rceil
n=⌊ay′×gcd(a,b)⌋−⌈b−x′×gcd(a,b)⌉
观察计算
x
,
y
x,y
x,y 的公式,发现
t
t
t 变化时,
x
,
y
x,y
x,y 的变化趋势正好相反
当
t
t
t 取到两个边界最值时,分别算出答案。
如果没有整数解,
t
t
t 取到两个边界最值时,得到答案。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
ll T,a,b,c,tmpgcd,nowx,nowy,lt,rt,ansx,ansy;
ll minx,miny,maxx,maxy,tx,ty,cnt;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
ll res = exgcd(b,a%b,x,y);
ll tmp = x;
x = y;
y = tmp-(a/b)*y;
return res;
}
int main()
{
scanf("%lld",&T);
for(;T >= 1;T --)
{
scanf("%lld%lld%lld",&a,&b,&c);
tmpgcd = exgcd(a,b,nowx,nowy);
if(c%tmpgcd > 0)
{
printf("-1\n");
continue;
}
ansx = nowx*c/tmpgcd;
ansy = nowy*c/tmpgcd;
tx = b/tmpgcd;
ty = a/tmpgcd;
lt = ll(ceil(double((double(-ansx)+1.00)/(double(tx)))));
rt = ll(floor(double((double(ansy)-1.00)/(double(ty)))));
// printf("%lld %lld\n",lt,rt);
if(lt <= rt)
{
cnt = rt-lt+1;
minx = ansx + tx*lt;
maxy = ansy - ty*lt;
maxx = ansx + tx*rt;
miny = ansy - ty*rt;
printf("%lld %lld %lld %lld %lld\n",cnt,minx,miny,maxx,maxy);
}
else
{
ansx = ansx + tx*lt;
ansy = ansy - ty*rt;
printf("%lld %lld\n",ansx,ansy);
}
}
return 0;
}
逆元
若
a
x
≡
1
(
m
o
d
b
)
ax\equiv 1 \pmod b
ax≡1(modb),
a
⊥
b
a\perp b
a⊥b (即
a
a
a 和
b
b
b 互质),则称
x
x
x 为
a
a
a 的逆元,记为
a
−
1
a^{-1}
a−1
当求
t
a
m
o
d
p
\dfrac{t}{a} \bmod p
atmodp 时,将其转化为
t
×
a
−
1
m
o
d
b
t\times a^{-1} \bmod b
t×a−1modb
求逆元的几种方法:
https://oi-wiki.org/math/inverse/
- 扩展欧几里得算法求解
显然可以将线性同余方程转化为二元一次不定方程,用扩展欧几里得求解
由于线性同余方程等价于不定方程,即 a x ≡ 1 ( m o d b ) ⇔ a x + b y = 1 ax\equiv 1 \pmod b \Leftrightarrow ax + by = 1 ax≡1(modb)⇔ax+by=1。所以求 a − 1 a^{-1} a−1 可以用扩展欧几里得算法。 - 另外,求逆元还有一个线性算法:
首先,显然可以知道 1 − 1 ≡ 1 ( m o d p ) 1^{-1} \equiv 1 \pmod p 1−1≡1(modp)(因为 1 − 1 = 1 1^{-1}=1 1−1=1)。
设 p = k × i + r , r < i , 1 < i < p p = k\times i+r,r < i,1<i<p p=k×i+r,r<i,1<i<p ,将这条式子放到 m o d p \bmod \space p mod p 意义下,得到: k × i + r ≡ 0 ( m o d p ) k\times i+r\equiv 0 \pmod p k×i+r≡0(modp)。
两边同乘 i − 1 , r − 1 i^{-1},r^{-1} i−1,r−1 ,得到:
k × r − 1 + i − 1 ≡ 0 ( m o d p ) i − 1 ≡ − k × r − 1 ( m o d p ) i − 1 ≡ − ⌊ p i ⌋ × ( p m o d i ) − 1 ( m o d p ) \begin{aligned} k\times r^{-1}+i^{-1} &\equiv 0 &\pmod p \\ i^{-1} &\equiv -k\times r^{-1} &\pmod p\\ i^{-1} &\equiv -\lfloor\dfrac{p}{i}\rfloor\times(p\bmod i)^{-1} &\pmod p \end{aligned} k×r−1+i−1i−1i−1≡0≡−k×r−1≡−⌊ip⌋×(pmodi)−1(modp)(modp)(modp)
第一步推导运用同余运算的同乘性,第二步推导运用同余运算的同加性,第三步推导中由先前设的 p = k × i + r p=k\times i + r p=k×i+r 显然得到 k = ⌊ p i ⌋ k = \lfloor\dfrac{p}{i}\rfloor k=⌊ip⌋ , r = p m o d i r = p \bmod i r=pmodi。
由此得到递推式用代码表示:inv[i] = -(p/i) * inv[p%i] = (long long)(p - p / i) * inv[p%i] % p;
- 求任意
n
n
n 个数的逆元
计算 n n n 个数的前缀积,对于 1 1 1 到 i i i 的前缀积记为 s i s_i si ,然后用扩展欧几里得法计算 s i s_i si 的逆元,记为 s v i sv_i svi 。
因为 s v n sv_n svn 是 n n n 个数的积的逆元,所以当我们把它乘上 a n a_n an 时,就会和 a n a_n an 的逆元抵消( s v n × a n = a 1 − 1 × a 2 − 1 × a 3 − 1 × ⋯ × a n − 1 × a n sv_{n}\times a^{n}=a{_1}^{-1}\times a_2^{-1} \times a_3^{-1} \times \cdots \times a_n^{-1} \times a_n svn×an=a1−1×a2−1×a3−1×⋯×an−1×an),于是就得到了 a 1 a_1 a1 到 a n − 1 a_{n-1} an−1 的积逆元,记为 s v n − 1 sv_{n-1} svn−1 。
同理我们可以依次计算出所有的 s v i sv_i svi ,于是 a − 1 a^{-1} a−1 就可以用 s v i × s i − 1 sv_{i}\times s_{i-1} svi×si−1 求得。
所以我们就在 O ( n + log p ) O(n+\log p) O(n+logp) 的时间内计算出了 n n n 个数的逆元。 - 由费马小定理 a p ≡ a ( m o d p ) , a p − 1 ≡ 1 ( m o d p ) a^p \equiv a\pmod p,a^{p-1}\equiv 1\pmod p ap≡a(modp),ap−1≡1(modp),故得到 a a a 的逆元为 a p − 2 a^{p-2} ap−2
P3811 【模板】乘法逆元
显然用线性算法就能够算出 1 1 1 到 n n n 的所有逆元了。
P5431 【模板】乘法逆元2
∑ i = 1 n k i a i ≡ ∑ i = 1 n ( k i ∏ j = 1 n a j a i ) ∏ i = 1 n a i ( m o d p ) ≡ ∑ i = 1 n ( k i × a i − 1 × ∏ j = 1 n a j ) × ∏ i = 1 n a i − 1 ( m o d p ) \begin{aligned}\sum_{i=1}^{n}\dfrac{k_i}{a_i}&\equiv\dfrac{\sum_{i=1}^{n}(k_i\dfrac{\prod_{j=1}^{n}a_j}{a_i})}{\prod_{i=1}^{n}a_i} &\pmod p\\ & \equiv \sum_{i=1}^{n}\left(k_i\times a_i^{-1}\times \prod_{j=1}^{n}a_j\right)\times \prod_{i=1}^{n}a_i^{-1}&\pmod p\end{aligned} i=1∑naiki≡∏i=1nai∑i=1n(kiai∏j=1naj)≡i=1∑n(ki×ai−1×j=1∏naj)×i=1∏nai−1(modp)(modp)
对式子变形之后,用前缀积的方式求乘积逆元 ∏ i = 1 n a i − 1 \prod_{i=1}^{n}a_i^{-1} ∏i=1nai−1 ,再维护一个后缀积,求 a i − 1 × ∏ j = 1 n a j = pres i − 1 × sufs i + 1 a_i^{-1}\times \prod_{j=1}^{n}a_j = \text{pres}_{i-1}\times \text{sufs}_{i+1} ai−1×∏j=1naj=presi−1×sufsi+1,然后这道题就可以高效完成了。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int n,p,k;
int a[5000005];
ll mul[5000005],smul[5000005];
int tmp,tmpgcd;
int inv,ans,kk;
char ch;
inline void myread(int &x){
x=0;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar())
x=(x<<3)+(x<<1)+(ch&15);
}
int exgcd(int a,int b,int &x,int &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
int res = exgcd(b,a%b,x,y);
int tmpx = x;
x = y;
y = tmpx-(a/b)*y;
return res;
}
int main()
{
myread(n);myread(p);myread(k);
for(int i = 1;i <= n;i ++) myread(a[i]);
mul[0] = 1;for(int i = 1;i <= n;i ++) mul[i] = ll(mul[i-1])*ll(a[i])%p;
smul[n+1] = 1;for(int i = n;i >= 1;i --) smul[i] = ll(smul[i+1])*ll(a[i])%p;
tmpgcd = exgcd(mul[n],p,inv,tmp);inv = (inv%p+p)%p;
kk = 1;
for(int i = 1;i <= n;i ++)
{
kk = ll(kk)*k%p;
ans = (ans+(ll(kk)*ll(mul[i-1]))%p*ll(smul[i+1])%p)%p;
}
printf("%lld",ll(ll(ans)*ll(inv)%p));
return 0;
}
中国剩余定理
CRT 可解决的线性同余方程组具有以下一般形式:
m
1
⊥
m
2
⊥
⋯
⊥
m
r
{
x
≡
b
1
(
m
o
d
m
1
)
x
≡
b
2
(
m
o
d
m
2
)
⋮
x
≡
b
r
(
m
o
d
m
r
)
m_1 \perp m_2 \perp \cdots \perp m_r\\ \begin{cases} x \equiv b_1 &\pmod {m_1} \\ x \equiv b_2 &\pmod {m_2} \\ \vdots \\ x \equiv b_r &\pmod {m_r} \\ \end{cases}
m1⊥m2⊥⋯⊥mr⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡b1x≡b2⋮x≡br(modm1)(modm2)(modmr)
算法思路:
- 计算所有模数的积 M = m 1 × m 2 × ⋯ × m r M=m_1\times m_2\times \cdots \times m_r M=m1×m2×⋯×mr。
- 枚举方程组中每一条方程,并构造关于这条方程的新方程组。
- 枚举到第
i
i
i 条方程,则有方程组:
{ x ≡ 0 ( m o d m 1 ) x ≡ 0 ( m o d m 2 ) ⋮ x ≡ 1 ( m o d m i ) ⋮ x ≡ 0 ( m o d m r ) \begin{cases}x \equiv 0 &\pmod {m_1} \\ x \equiv 0 &\pmod {m_2} \\ \vdots \\ x \equiv 1 &\pmod {m_i} \\ \vdots \\ x \equiv 0 &\pmod {m_r} \\ \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧x≡0x≡0⋮x≡1⋮x≡0(modm1)(modm2)(modmi)(modmr) - 此时设 t i = M m i t_i = \dfrac{M}{m_i} ti=miM (对于以上方程组特殊解 t i t_i ti 必然是其他模数的最小公倍数),求 t i t_i ti 在模 m i m_i mi 意义下的逆元 t − 1 t^{-1} t−1
- 设 c i = t i × t i − 1 c_i = t_i\times t_i^{-1} ci=ti×ti−1 ( 因为 c i ≡ 1 ( m o d M ) c_i\equiv 1\pmod M ci≡1(modM) )
- 最后的方程组唯一解: x = ∑ i = 1 r b i c i ( m o d M ) x=\sum_{i=1}^{r} {b_ic_i\pmod M} x=∑i=1rbici(modM)(根据同余运算的同加性得到最后的唯一解)
P1495 【模板】中国剩余定理(CRT)/曹冲养猪
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
ll n,a[25],b[25],c,m,invm,tmp,tmpy,ans;
ll s;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
ll res = exgcd(b,a%b,x,y);
ll tmpx = x;
x = y;
y = tmpx-(a/b)*y;
return res;
}
ll CRT()//Chinese Remainder Theorem
{
s = 1;ans = 0;
for(int i = 1;i <= n;i ++) s *= b[i];
for(int i = 1;i <= n;i ++)
{
m = s/b[i];//x = my
//my _= 1 (mod b[i]) <=> my + b[i]k = 1
tmp = exgcd(m,b[i],invm,tmpy);//m^(-1)
c = m*invm;
ans = (ans+a[i]*c)%s;
}
if(ans <= 0) ans += s;
return ans;
}
int main()
{
scanf("%lld",&n);
for(int i = 1;i <= n;i ++) scanf("%lld%lld",&b[i],&a[i]);// x _= a[i] (mod b[i])
printf("%lld",CRT());
return 0;
}
P3868 [TJOI2009]猜数字
P3868 [TJOI2009]猜数字
b
∣
(
n
−
a
i
)
⇔
n
≡
a
i
(
m
o
d
b
)
b|(n-a_i) \Leftrightarrow n\equiv a_i\pmod b
b∣(n−ai)⇔n≡ai(modb)
然后套板子做完了。
不过要注意:两数相乘的时候往往来不及取模就爆long long
了,使用技巧快速乘:
分解乘数,及时取模
ll mul(ll a,ll b){//快速乘
ll r=0;
while(b){
if(b&1) r= (r+a)%N;//改乘为加,好“快”啊
a = (a+a)%N;
b>>=1;
}
return r;
}
UVA756 Biorhythms
UVA756 Biorhythms
比板子还板子。
扩展中国剩余定理(未完成)
详细讲解见别人的 blog:https://www.luogu.com.cn/blog/blue/kuo-zhan-zhong-guo-sheng-yu-ding-li
ExCRT 可解决的线性同余方程组具有以下一般形式:
{
x
≡
b
1
(
m
o
d
m
1
)
x
≡
b
2
(
m
o
d
m
2
)
⋮
x
≡
b
r
(
m
o
d
m
r
)
\begin{cases} x \equiv b_1 &\pmod {m_1} \\ x \equiv b_2 &\pmod {m_2} \\ \vdots \\ x \equiv b_r &\pmod {m_r} \\ \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡b1x≡b2⋮x≡br(modm1)(modm2)(modmr)
算法思路:
通过将方程两两合并为一等价方程,从而将方程组压缩乘一条方程,求这条方程的解。
- 取出两条方程 { x ≡ b 1 ( m o d m 1 ) x ≡ b 2 ( m o d m 2 ) \begin{cases}x\equiv b_1&\pmod {m_1}\\x\equiv b_2&\pmod {m_2}\end{cases} {x≡b1x≡b2(modm1)(modm2)
- 改写方程 x = m 1 p + b 1 = m 2 q + b 2 ⇒ m 1 p − m 2 q = b 2 − b 1 x=m_1p+b_1=m_2q+b_2\Rightarrow m_1p-m_2q=b_2-b_1 x=m1p+b1=m2q+b2⇒m1p−m2q=b2−b1
- 计算
d
=
gcd
(
m
1
,
m
2
)
d=\gcd(m1,m2)
d=gcd(m1,m2) ,如果满足
d
∣
(
b
2
−
b
1
)
d|(b_2-b_1)
d∣(b2−b1) ,则方程有解,可以解出
x
x
x,否则方程无解,
整段垮掉整个方程组也就无解,算法结束。 - 现求 p p p 和 q q q,由…
\operatorname{lcm}(m_1,m_2)
欧拉定理
φ ( n ) = ∑ i = 1 n [ i ⊥ n ] a φ ( n ) + 1 ≡ a ( m o d n ) a k ≡ { a k if k ⩽ φ ( n ) a k m o d φ ( n ) + φ ( n ) otherwise ( m o d n ) \begin{aligned} \varphi (n) &= \sum_{i=1}^{n}[i\perp n]\\ a^{\varphi(n)+1} &\equiv a \pmod n\\ a^{k} &\equiv \begin{cases} a^k&\text{if}\;\; k\leqslant\varphi(n)\\ a^{k\bmod \varphi(n)+\varphi(n)}&\text{otherwise} \end{cases}\pmod n \end{aligned} φ(n)aφ(n)+1ak=i=1∑n[i⊥n]≡a(modn)≡{akakmodφ(n)+φ(n)ifk⩽φ(n)otherwise(modn)
素数
π
(
n
)
∼
n
ln
n
\pi (n) \sim \dfrac{n}{\ln n}
π(n)∼lnnn
p
n
∼
n
ln
n
p_n \sim n\ln n
pn∼nlnn
基本算术定理:
任何一个大于
1
1
1 的自然数 ,如果
N
N
N 不为质数,都可以唯一分解成有限个质数的乘积
N
=
p
1
a
1
×
p
2
a
2
×
⋯
×
p
k
a
k
N=p^{a_1}_1\times p^{a_2}_2\times\cdots\times p^{a_k}_k
N=p1a1×p2a2×⋯×pkak ,这里
p
j
(
1
⩽
j
⩽
k
)
p_j(1\leqslant j\leqslant k)
pj(1⩽j⩽k) 均为质数,其诸指数
a
j
a_j
aj 是正整数。
由此定理得到快速筛法:
for(int i = 2;i < N;i ++)
{
if(!isNotPrime[i])//当前i是一个素数
{
cnt ++;
prime[cnt] = i;//记录素数
}
for(int j = 1;j <= cnt && i*prime[j] < N;j ++)//根据基本算术定理构造合数
{
isNotPrime[i*prime[j]] = 1;
if(!(i%prime[j])) break;
}
}
Baby Step Giant Step (BSGS)
斐波那契数列
斐波那契数列一般形式
0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , ⋯ 0,1,1,2,3,5,8,13,21,\cdots 0,1,1,2,3,5,8,13,21,⋯
递推公式
F n = { 0 n = 0 1 n = 1 F n − 1 + F n − 2 n > 1 F_n= \begin{cases} 0 &n=0\\ 1 &n=1\\ F_{n-1} + F_{n-2} &n>1 \end{cases} Fn=⎩⎪⎨⎪⎧01Fn−1+Fn−2n=0n=1n>1
特殊性质:
- 卡西尼性质(Cassini’s identity): F n − 1 F n + 1 − F n 2 = ( − 1 ) n F_{n-1}F_{n+1}-F_n^2 = (-1)^n Fn−1Fn+1−Fn2=(−1)n 。
- 附加性质: F n + k = F k F n + 1 + F k − 1 F n F_{n+k}=F_kF_{n+1}+F_{k-1}F_n Fn+k=FkFn+1+Fk−1Fn 。
- 取上一条性质中 k = n k=n k=n ,我们得到 F 2 n = F n ( F n − 1 + F n + 1 ) F_{2n}=F_{n}(F_{n-1}+F_{n+1}) F2n=Fn(Fn−1+Fn+1) 。
- 由上一条性质可以归纳证明, ∀ k ∈ N , F n ∣ F n k \forall k\in\mathbb N,F_n|F_{nk} ∀k∈N,Fn∣Fnk 。
- 上述性质可逆,即 ∀ F a ∣ F b , a ∣ b \forall F_a|F_b,a|b ∀Fa∣Fb,a∣b 。
- GCD 性质: gcd ( F n , F m ) = F gcd ( n , m ) \gcd(F_n,F_m)=F_{\gcd(n,m)} gcd(Fn,Fm)=Fgcd(n,m) 。
- 以斐波那契数列相邻两项作为输入会使欧几里德算法达到最坏复杂度
快速倍增法求斐波那契数列任意项:
利用性质2,有:
F
2
n
=
F
n
(
2
F
n
+
1
−
F
n
)
F
2
n
+
1
=
F
n
+
1
2
+
F
n
2
F_{2n}=F_n(2F_{n+1}-F_n)\\F_{2n+1}=F_{n+1}^2+F_{n}^2
F2n=Fn(2Fn+1−Fn)F2n+1=Fn+12+Fn2
于是可以通过这样的方法快速计算两个相邻的斐波那契数(常数比矩乘小)。代码如下,返回值是一个二元组
(
F
n
,
F
n
+
1
)
(F_n,F_{n+1})
(Fn,Fn+1) 。
typedef long long ll;
pair<ll, ll> Fib(int n)
{
if (n == 0) return {0,1};
pair<ll ,ll> p;
p = Fib(n >> 1);
ll c = p.first * (2 * p.second - p.first);
ll d = p.first * p.first + p.second * p.second;
if (n % 2 == 1)
return {d, c + d};
else
return {c, d};
}
待定系数法推导通项公式:
设常数
r
r
r 和
s
s
s ,使得:
F
n
−
r
×
F
n
−
1
=
s
×
(
F
n
−
1
−
r
×
F
n
−
2
)
F_n-r\times F_{n-1}=s\times(F_{n-1}-r\times F_{n-2})
Fn−r×Fn−1=s×(Fn−1−r×Fn−2)因为
F
n
=
F
n
−
1
+
F
n
−
2
F_n = F_{n-1} + F_{n-2}
Fn=Fn−1+Fn−2
所以:
r
+
s
=
1
,
−
r
s
=
1
r+s=1,-rs=1
r+s=1,−rs=1 在
n
⩾
3
n\geqslant 3
n⩾3 时,有:
F
n
−
r
×
F
n
−
1
=
s
×
(
F
n
−
1
−
r
×
F
n
−
2
)
F
n
−
1
−
r
×
F
n
−
2
=
s
×
(
F
n
−
2
−
r
×
F
n
−
3
)
⋯
F
3
−
r
×
F
2
=
s
×
(
F
2
−
r
×
F
1
)
F_n-r\times F_{n-1}=s\times(F_{n-1}-r\times F_{n-2})\\F_{n-1}-r\times F_{n-2}=s\times(F_{n-2}-r\times F_{n-3})\\\cdots\\F_3-r\times F_{2}=s\times(F_{2}-r\times F_{1})
Fn−r×Fn−1=s×(Fn−1−r×Fn−2)Fn−1−r×Fn−2=s×(Fn−2−r×Fn−3)⋯F3−r×F2=s×(F2−r×F1)联立以上
n
−
2
n-2
n−2 个式子,得到
F
n
−
r
×
F
n
−
1
=
s
n
−
2
×
(
F
2
−
r
×
F
1
)
F_n-r\times F_{n-1}=s^{n-2}\times(F_{2}-r\times F_{1})
Fn−r×Fn−1=sn−2×(F2−r×F1)因为
s
=
1
−
r
,
F
1
=
F
2
=
1
s=1-r,F_1=F_2=1
s=1−r,F1=F2=1 ,所以有:
F
n
=
s
n
−
1
+
r
×
F
n
−
1
=
s
n
−
1
+
r
s
n
−
2
+
r
2
F
n
−
2
⋯
=
s
n
−
1
+
r
s
n
−
2
+
r
2
s
n
−
3
+
⋯
+
r
n
−
2
s
+
r
n
−
1
\begin{aligned}F_n &= s^{n-1}+r\times F_{n-1}\\ &= s^{n-1}+rs^{n-2}+r^2F_{n-2}\\&\cdots\\& = s^{n-1}+rs^{n-2}+r^2s^{n-3}+\cdots+r^{n-2}s+r^{n-1}\end{aligned}
Fn=sn−1+r×Fn−1=sn−1+rsn−2+r2Fn−2⋯=sn−1+rsn−2+r2sn−3+⋯+rn−2s+rn−1可以看出
F
n
F_n
Fn 是这个以
s
n
−
1
s^{n-1}
sn−1 为首项,以
r
n
−
1
r^{n-1}
rn−1 为末项,
r
s
\dfrac{r}{s}
sr 为公比的等比数列的各项和
对于一个等比数列
{
a
n
}
\{a_n\}
{an} ,如果公比是
q
,
q
≠
1
q,q\neq1
q,q=1 ,首项是
a
1
a_1
a1 ,那么前
k
k
k 项的和
S
k
=
a
1
(
1
−
q
k
)
1
−
q
=
a
1
−
a
1
q
k
1
−
q
S_k=\dfrac{a_1(1-q^k)}{1-q}=\dfrac{a_1-a_1q^k}{1-q}
Sk=1−qa1(1−qk)=1−qa1−a1qk
所以:
F
n
=
s
n
−
1
−
s
n
−
1
(
r
s
)
n
1
−
r
s
=
s
n
−
r
n
s
−
r
\begin{aligned}F_n&=\dfrac{s^{n-1}-s^{n-1}(\dfrac{r}{s})^{n}}{1-\dfrac{r}{s}}\\&=\dfrac{s^n-r^n}{s-r}\end{aligned}
Fn=1−srsn−1−sn−1(sr)n=s−rsn−rn对于方程组
{
r
+
s
=
1
−
r
s
=
1
\begin{cases}r+s&=1\\-rs&=1\end{cases}
{r+s−rs=1=1可以解得:
{
r
=
1
+
5
2
s
=
1
−
5
2
\begin{cases}r&=\dfrac{1+\sqrt{5}}{2}\\s&=\dfrac{1-\sqrt{5}}{2}\end{cases}
⎩⎪⎨⎪⎧rs=21+5=21−5
所以,斐波那契数列的通项公式就是
F
n
=
5
5
[
(
1
+
5
2
)
n
−
(
1
−
5
2
)
n
]
F_n=\dfrac{\sqrt{5}}{5}\left[\left(\dfrac{1+\sqrt{5}}{2}\right)^n-\left(\dfrac{1-\sqrt{5}}{2}\right)^n\right]
Fn=55[(21+5)n−(21−5)n]
P1306 斐波那契公约数
P1306 斐波那契公约数
利用 GCD 性质并使用快速倍增法
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const long long modn = 1e8;
typedef long long ll;
ll n,m,tmpgcd;
ll gcd(ll a,ll b)
{
return b == 0 ? a:gcd(b,a%b);
}
pair<ll, ll> Fib(int n)
{
if (n == 0) return make_pair(0,1);
pair<ll ,ll> p;
p = Fib(n >> 1);
ll f1 = p.first * (2 * p.second%modn - p.first%modn + modn)%modn;
ll f2 = (p.first * p.first %modn + p.second * p.second%modn)%modn;
if (n % 2 == 1)
return make_pair(f2%modn, (f1 + f2)%modn);
else
return make_pair(f1%modn, f2%modn);
}
int main()
{
scanf("%lld%lld",&n,&m);
tmpgcd = gcd(n,m);
pair<ll , ll> ans;
ans = Fib(tmpgcd);
printf("%lld",ans.first%modn);
return 0;
}
参考资料:
OI WIKI 数学
《信息学竞赛之数学一本通》