趁现在脑子还好使赶快复习一波小学生都会的高中数学
欧几里得算法
真の欧几里得算法
计算两个整数
a
,
b
a,b
a , b 的最大公约数。 时间复杂度:
O
(
log
2
n
)
O(\log_2n)
O ( log 2 n )
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
证明:
a
m
o
d
  
b
a\mod b
a m o d b 可以表示成
a
−
k
b
,
k
=
⌊
a
b
⌋
a-kb,k=\lfloor\frac ab\rfloor
a − k b , k = ⌊ b a ⌋ 。 设
d
d
d 是
a
,
b
a,b
a , b 的一个公约数,则有
d
∣
a
,
d
∣
b
d|a,d|b
d ∣ a , d ∣ b 。 设
a
=
x
d
,
b
=
y
d
,
x
,
y
∈
Z
a=xd,b=yd,x,y\in Z
a = x d , b = y d , x , y ∈ Z
∴
a
m
o
d
  
b
=
a
−
k
b
=
x
d
−
k
y
d
=
(
x
−
k
y
)
d
\therefore a\mod b=a-kb=xd-kyd=(x-ky)d
∴ a m o d b = a − k b = x d − k y d = ( x − k y ) d
∵
x
−
k
y
\because x-ky
∵ x − k y 是整数,
∴
d
∣
r
\therefore d|r
∴ d ∣ r
∴
d
\therefore d
∴ d 是
b
b
b 和
a
m
o
d
  
b
a\mod b
a m o d b 的公约数。
扩展欧几里得算法
在已知整数
a
,
b
a,b
a , b 的情况下求
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
a x + b y = g c d ( a , b ) 的一组整数解
a
,
b
a,b
a , b 。 时间复杂度:
O
(
log
2
n
)
O(\log_2n)
O ( log 2 n )
void ext_gcd(int a,int b,int&x,int&y){
if(!b){x=1,y=0;return;}
ext_gcd(b,a%b,y,x);y-=a/b*x;
}
证明: 设
a
x
1
+
b
y
1
=
g
c
d
(
a
,
b
)
,
b
x
2
+
(
a
m
o
d
  
b
)
y
2
=
g
c
d
(
b
,
a
m
o
d
  
b
)
ax_1+by_1=gcd(a,b),bx_2+(a\mod b)y_2=gcd(b,a\mod b)
a x 1 + b y 1 = g c d ( a , b ) , b x 2 + ( a m o d b ) y 2 = g c d ( b , a m o d b ) 由欧几里得算法得
a
x
1
+
b
y
1
=
b
x
2
+
(
a
m
o
d
  
b
)
y
2
ax_1+by_1=bx_2+(a\mod b)y_2
a x 1 + b y 1 = b x 2 + ( a m o d b ) y 2 设
a
m
o
d
  
b
=
a
−
k
b
,
k
=
⌊
a
b
⌋
a\mod b=a-kb,k=\lfloor\frac ab\rfloor
a m o d b = a − k b , k = ⌊ b a ⌋
∴
a
x
1
+
b
y
1
=
b
x
2
+
(
a
−
k
b
)
y
2
\therefore ax_1+by_1=bx_2+(a-kb)y_2
∴ a x 1 + b y 1 = b x 2 + ( a − k b ) y 2 转换得到
a
x
1
+
b
y
1
=
a
y
2
+
b
(
x
2
−
k
y
2
)
ax_1+by_1=ay_2+b(x_2-ky_2)
a x 1 + b y 1 = a y 2 + b ( x 2 − k y 2 ) 观察上式可知
{
x
1
=
y
2
y
1
=
x
2
−
⌊
a
b
⌋
y
2
\begin{cases}x_1=y_2\\y_1=x_2-\lfloor\frac ab\rfloor y_2\end{cases}
{ x 1 = y 2 y 1 = x 2 − ⌊ b a ⌋ y 2
g
c
d
(
a
,
b
)
gcd(a,b)
g c d ( a , b ) 中
b
=
0
b=0
b = 0 时,
{
x
=
1
y
=
0
\begin{cases}x=1\\y=0\end{cases}
{ x = 1 y = 0 为该方程的一组解,此时回溯回去就可求出
x
,
y
x,y
x , y 最初的解了。
如果要同时求解不定方程和求
g
c
d
gcd
g c d ,可以用下面的操作:
int ext_gcd(int a,int b,int&x,int&y){
if(!b){x=1,y=0;return a;}
int d=ext_gcd(b,a%b,y,x);
y-=a/b*x;return d;
}
解不定方程
对于不定方程
a
x
+
b
y
=
c
ax+by=c
a x + b y = c ,设
g
c
d
(
a
,
b
)
=
d
gcd(a,b)=d
g c d ( a , b ) = d 当
c
m
o
d
  
d
=
0
c\mod d=0
c m o d d = 0 时有通解:
{
x
=
c
d
x
′
+
b
d
k
y
=
c
d
y
′
−
a
d
k
,
k
∈
Z
\begin{cases}x=\frac cdx'+\frac bdk\\y=\frac cdy'-\frac adk\end{cases},k\in Z
{ x = d c x ′ + d b k y = d c y ′ − d a k , k ∈ Z
这里有一道题: 数学题(NKOJ 5028)
问题描述 给出
a
,
b
,
c
,
x
1
,
x
2
,
y
1
,
y
2
a,b,c,x_1,x_2,y_1,y_2
a , b , c , x 1 , x 2 , y 1 , y 2 ,求满足
a
x
+
b
y
+
c
=
0
ax+by+c=0
a x + b y + c = 0 ,且
x
∈
[
x
1
,
x
2
]
,
y
∈
[
y
1
,
y
2
]
x\in[x_1,x_2],y\in[y_1,y_2]
x ∈ [ x 1 , x 2 ] , y ∈ [ y 1 , y 2 ] 的整数解有多少对。 输入格式 第一行包含
7
7
7 个整数,
a
,
b
,
c
,
x
1
,
x
2
,
y
1
,
y
2
a,b,c,x_1,x_2,y_1,y_2
a , b , c , x 1 , x 2 , y 1 , y 2 ,整数间用空格隔开。
a
,
b
,
c
,
x
1
,
x
2
,
y
1
,
y
2
a,b,c,x_1,x_2,y_1,y_2
a , b , c , x 1 , x 2 , y 1 , y 2 的绝对值不超过
1
0
8
10^8
1 0 8 。 输出格式 输出整数解有多少对。 样例输入 1 1 -3 0 4 0 4 样例输出 4
首先处理区间不存在或
a
=
0
a=0
a = 0 或
b
=
0
b=0
b = 0 的情形,直接根据区间大小计算出解的个数。 若
a
,
b
a,b
a , b 均不为
0
0
0 ,原方程即为不定方程,用扩展欧几里得算法求解。 若有解,求出一组解
x
,
y
x,y
x , y ,根据
x
1
⩽
x
⩽
x
2
x_1\leqslant x\leqslant x_2
x 1 ⩽ x ⩽ x 2 ,得
d
b
>
0
\frac db>0
b d > 0 时,
d
b
(
x
1
−
x
)
⩽
k
x
⩽
d
b
(
x
2
−
x
)
\frac db(x_1-x)\leqslant k_x\leqslant\frac db(x_2-x)
b d ( x 1 − x ) ⩽ k x ⩽ b d ( x 2 − x )
d
b
<
0
\frac db<0
b d < 0 时,
d
b
(
x
2
−
x
)
⩽
k
x
⩽
d
b
(
x
1
−
x
)
\frac db(x_2-x)\leqslant k_x\leqslant\frac db(x_1-x)
b d ( x 2 − x ) ⩽ k x ⩽ b d ( x 1 − x ) 同理求出
k
y
k_y
k y 的范围,取两范围的交集,其中整数的个数即为解。
typedef long long LL;
LL ext_gcd(LL a,LL b,LL&x1,LL&y1);
int main(){
LL a,b,c,d,x1,x2,y1,y2,x,y;
read(&a,&b,&c,&x1,&x2,&y1,&y2);
c=-c;
if(x1>x2||y1>y2)return write("0"),0;
if(!a){
if(!b){
if(!c)return write((x2-x1+1)*(y2-y1+1)),0;
return write("0"),0;
}
if(!c)return write(y1<=0&&0<=y2?x2-x1+1:0),0;
return write(!(c%b)&&y1<=c/b&&c/b<=y2?x2-x1+1:0),0;
}
if(!b){
if(!c)return write(x1<=0&&0<=x2?y2-y1+1:0),0;
return write(!(c%a)&&x1<=c/a&&c/a<=x2?y2-y1+1:0),0;
}
d=ext_gcd(a,b,x,y);
if(c%d)return write("0"),0;
x*=c/d,y*=c/d;
double tx1=(x1-x)*1.0*d/b,tx2=(x2-x)*1.0*d/b;
if((double)d/b<0)swap(tx1,tx2);
LL kx1=(LL)ceil(tx1),kx2=(LL)floor(tx2);
double ty1=(y-y2)*1.0*d/a,ty2=(y-y1)*1.0*d/a;
if((double)d/a<0)swap(ty1,ty2);
LL ky1=(LL)ceil(ty1),ky2=(LL)floor(ty2);
LL k1=max(kx1,ky1),k2=min(kx2,ky2);
write(k1<=k2?k2-k1+1:0);
return 0;
}
解模线性方程
a
x
≡
b
(
m
o
d
n
)
ax\equiv b\pmod n
a x ≡ b ( m o d n ) 的含义是
a
x
m
o
d
  
n
=
b
m
o
d
  
n
ax\mod n=b\mod n
a x m o d n = b m o d n 即
a
x
−
b
ax-b
a x − b 是
n
n
n 的整数倍,设这个倍数为
y
y
y ,则
a
x
−
b
=
n
y
ax-b=ny
a x − b = n y ,移项得
a
x
−
n
y
=
b
ax-ny=b
a x − n y = b
求乘法逆元
a
x
≡
1
(
m
o
d
n
)
ax\equiv 1\pmod n
a x ≡ 1 ( m o d n ) 的解
x
x
x 称为
a
a
a 关于模
n
n
n 的乘法逆元。 在
g
c
d
(
a
,
n
)
=
1
gcd(a,n)=1
g c d ( a , n ) = 1 的前提下,
a
x
≡
1
(
m
o
d
n
)
ax\equiv1\pmod n
a x ≡ 1 ( m o d n ) 只有唯一解,此解即为
a
a
a 在模
n
n
n 下的乘法逆元。
中国剩余定理
真の中国剩余定理
求解下列方程组(
w
1
,
w
2
,
…
,
w
k
w_1,w_2,\ldots,w_k
w 1 , w 2 , … , w k 两两互质):
{
x
≡
b
1
(
m
o
d
w
1
)
x
≡
b
2
(
m
o
d
w
2
)
⋮
x
≡
b
k
(
m
o
d
w
k
)
\begin{cases} x&\equiv b_1\pmod{w_1}\\ x&\equiv b_2\pmod{w_2}\\ &\vdots\\ x&\equiv b_k\pmod{w_k} \end{cases}
⎩ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎧ x x x ≡ b 1 ( m o d w 1 ) ≡ b 2 ( m o d w 2 ) ⋮ ≡ b k ( m o d w k )
上面方程组的解为
x
=
∑
i
=
1
k
m
i
m
i
−
1
b
i
m
o
d
  
p
x=\sum_{i=1}^km_i{m_i}^{-1}b_i\mod p
x = i = 1 ∑ k m i m i − 1 b i m o d p
其中
p
=
∏
i
=
1
k
w
i
,
m
i
=
p
w
i
,
m
i
m
i
−
1
≡
1
(
m
o
d
w
i
)
p=\prod_{i=1}^kw_i,m_i=\frac p{w_i},m_i{m_i}^{-1}\equiv1\pmod{w_i}
p = ∏ i = 1 k w i , m i = w i p , m i m i − 1 ≡ 1 ( m o d w i )
int crt(int*b,int*w,int k){
int x,y,ans=0,mi,p=1;
for(int i=1;i<=k;i++)p*=w[i];
for(int i=1;i<=k;i++){
mi=p/w[i];
ext_gcd(mi,w[i],x,y);
ans=(ans+x*mi*b[i])%p;
}
return ans?ans:ans+p;
}
证明略
扩展中国剩余定理
w
1
,
w
2
,
…
,
w
n
w_1,w_2,\ldots,w_n
w 1 , w 2 , … , w n 为不一定两两互质的整数,求
x
x
x 的最小非负整数解。
int ext_crt(int*b,int*w,int n){
int ans=0,m=1,p;
for(int i=1;i<=n;i++){
int x,y,d=ext_gcd(m,w[i],x,y),t=w[i]/d;
if((b[i]-ans)%d)return -1;
x=(x%t+t)%t,p=m*t;
int k=((b[i]-ans)/d*x%t+t)%t;
ans=(ans+k*m)%p,m=p;
}
return ans?ans:ans+p;
}
证明: 设前
n
−
1
n-1
n − 1 个同余方程组成的方程组的一个解为
x
′
x'
x ′ ,
m
=
∏
i
=
1
n
−
1
w
i
m=\prod_{i=1}^{n-1}w_i
m = ∏ i = 1 n − 1 w i 则其通解为
x
=
x
′
+
k
m
,
k
∈
Z
x=x'+km,k\in Z
x = x ′ + k m , k ∈ Z 则需找到一个正整数
k
′
k'
k ′ 满足第
n
n
n 个方程,即
x
′
+
k
′
m
≡
b
n
(
m
o
d
w
n
)
x'+k'm\equiv b_n\pmod{w_n}
x ′ + k ′ m ≡ b n ( m o d w n ) 转化得
k
′
m
≡
b
n
−
x
′
(
m
o
d
w
n
)
k'm\equiv b_n-x'\pmod{w_n}
k ′ m ≡ b n − x ′ ( m o d w n ) ,用扩展欧几里得求解。
这里又有一道题: 洗牌(NKOJ 4034)
问题描述 BH发明了一台洗牌机。机器上有一排共
N
N
N 个格子,一开始,我们将编号
1
1
1 到
N
N
N 的
N
N
N 张牌依次放入这
N
N
N 个格子里。每个格子下方标识了一个数字,表示经过一次洗牌后,对应格子出现的牌的编号。 现在给你
N
N
N 张牌和洗牌后要求得到的顺序,问洗牌机需要进行多少次洗牌才能得到指定牌序?如果无解,输出
−
1
-1
− 1 。 输入格式 第一行,一个整数
N
N
N (
1
⩽
N
⩽
520
1\leqslant N\leqslant520
1 ⩽ N ⩽ 5 2 0 )。 第二行,
N
N
N 个空格间隔的整数,表示洗牌机1到N每个格子下对应的标识。 第三行,
N
N
N 个空格间隔的整数,表示指定的牌序。 输出格式 一行,一个整数,表示所需洗牌次数。无解输出
−
1
-1
− 1 。 样例输入 4 2 3 4 1 3 4 1 2 样例输出 2
显然在不断洗牌的过程中,每个位置上牌的编号是循环出现的。 记
c
[
i
]
[
j
]
c[i][j]
c [ i ] [ j ] 为
i
i
i 位置出现编号为
j
j
j 的牌所需的最少洗牌次数(规定
c
[
i
]
[
i
]
>
0
c[i][i]>0
c [ i ] [ i ] > 0 ,则
c
[
i
]
[
i
]
c[i][i]
c [ i ] [ i ] 表示
i
i
i 位置牌的编号循环一次所需的次数),
a
[
i
]
a[i]
a [ i ] 为指定牌序中
i
i
i 位置牌的编号,则答案为下面方程组的最小非负整数解:
{
x
≡
c
[
1
]
[
a
[
1
]
]
(
m
o
d
c
[
1
]
[
1
]
)
x
≡
c
[
2
]
[
a
[
2
]
]
(
m
o
d
c
[
2
]
[
2
]
)
⋮
x
≡
c
[
n
]
[
a
[
n
]
]
(
m
o
d
c
[
n
]
[
n
]
)
\begin{cases} x&\equiv c[1][a[1]]\pmod{c[1][1]}\\ x&\equiv c[2][a[2]]\pmod{c[2][2]}\\ &\vdots\\ x&\equiv c[n][a[n]]\pmod{c[n][n]} \end{cases}
⎩ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎧ x x x ≡ c [ 1 ] [ a [ 1 ] ] ( m o d c [ 1 ] [ 1 ] ) ≡ c [ 2 ] [ a [ 2 ] ] ( m o d c [ 2 ] [ 2 ] ) ⋮ ≡ c [ n ] [ a [ n ] ] ( m o d c [ n ] [ n ] )
c
[
i
]
[
j
]
c[i][j]
c [ i ] [ j ] 数组直接暴力模拟,然后用扩展中国剩余定理求解即可。
const int maxn=520+5;
int c[maxn][maxn],mk[maxn],a[maxn],n;
int ext_gcd(int a,int b,int&x,int&y);
int ext_crt(int*b,int*w,int n);
int b[maxn],w[maxn];
int main(){
read(&n);
for(int i=1;i<=n;i++)read(&mk[i]);
for(int i=1;i<=n;i++)read(&a[i]);
for(int i=1;i<=n;i++){
int t=i,cnt=1;
while(mk[t]!=i)t=mk[t],c[i][t]=cnt++;
c[i][i]=cnt;
}
for(int i=1;i<=n;i++)b[i]=c[i][a[i]],w[i]=c[i][i];
write(ext_crt(b,w,n),"\n");
return 0;
}