【数论】欧几里得算法与中国剩余定理

版权声明:来自达羌蒟蒻 https://blog.csdn.net/PHenning/article/details/89041940

趁现在脑子还好使赶快复习一波小学生都会的高中数学

欧几里得算法

真の欧几里得算法

计算两个整数 a , b a,b 的最大公约数。
时间复杂度: O ( log 2 n ) O(\log_2n)

int gcd(int a,int b){
  return b?gcd(b,a%b):a;
}

证明:
a m o d    b a\mod b 可以表示成 a k b , k = a b a-kb,k=\lfloor\frac ab\rfloor
d d a , b a,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 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
x k y \because x-ky 是整数, d r \therefore d|r
d \therefore d b b a m o d    b a\mod b 的公约数。

扩展欧几里得算法

在已知整数 a , b a,b 的情况下求 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) 的一组整数解 a , b a,b
时间复杂度: O ( log 2 n ) O(\log_2n)

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 = b x 2 + ( a m o d    b ) y 2 ax_1+by_1=bx_2+(a\mod 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 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 = a y 2 + b ( x 2 k y 2 ) ax_1+by_1=ay_2+b(x_2-ky_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}
g c d ( a , b ) gcd(a,b) b = 0 b=0 时, { x = 1 y = 0 \begin{cases}x=1\\y=0\end{cases} 为该方程的一组解,此时回溯回去就可求出 x , y x,y 最初的解了。

如果要同时求解不定方程和求 g c d gcd ,可以用下面的操作:

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 ,设 g c d ( a , b ) = d gcd(a,b)=d
c m o d    d = 0 c\mod 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

这里有一道题:
数学题(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 x + b y + c = 0 ax+by+c=0 ,且 x [ x 1 , x 2 ] , y [ y 1 , y 2 ] x\in[x_1,x_2],y\in[y_1,y_2] 的整数解有多少对。

输入格式
第一行包含 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 的绝对值不超过 1 0 8 10^8

输出格式
输出整数解有多少对。

样例输入
1 1 -3 0 4 0 4

样例输出
4

首先处理区间不存在或 a = 0 a=0 b = 0 b=0 的情形,直接根据区间大小计算出解的个数。
a , b a,b 均不为 0 0 ,原方程即为不定方程,用扩展欧几里得算法求解。
若有解,求出一组解 x , y x,y ,根据 x 1 x x 2 x_1\leqslant x\leqslant x_2 ,得
d b > 0 \frac db>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)
d b < 0 \frac db<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)
同理求出 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 m o d &ThinSpace;&ThinSpace; n = b m o d &ThinSpace;&ThinSpace; n ax\mod n=b\mod n
a x b ax-b n n 的整数倍,设这个倍数为 y y ,则 a x b = n y ax-b=ny ,移项得 a x n y = b ax-ny=b

求乘法逆元

a x 1 ( m o d n ) ax\equiv 1\pmod n 的解 x x 称为 a a 关于模 n n 的乘法逆元。
g c d ( a , n ) = 1 gcd(a,n)=1 的前提下, a x 1 ( m o d n ) ax\equiv1\pmod n 只有唯一解,此解即为 a a 在模 n n 下的乘法逆元。

中国剩余定理

真の中国剩余定理

求解下列方程组( w 1 , w 2 , , w k w_1,w_2,\ldots,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&amp;\equiv b_1\pmod{w_1}\\ x&amp;\equiv b_2\pmod{w_2}\\ &amp;\vdots\\ x&amp;\equiv b_k\pmod{w_k} \end{cases}

上面方程组的解为 x = i = 1 k m i m i 1 b i m o d &ThinSpace;&ThinSpace; p x=\sum_{i=1}^km_i{m_i}^{-1}b_i\mod 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}

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 为不一定两两互质的整数,求 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 个同余方程组成的方程组的一个解为 x x&#x27; m = i = 1 n 1 w i m=\prod_{i=1}^{n-1}w_i
则其通解为 x = x + k m , k Z x=x&#x27;+km,k\in Z
则需找到一个正整数 k k&#x27; 满足第 n n 个方程,即 x + k m b n ( m o d w n ) x&#x27;+k&#x27;m\equiv b_n\pmod{w_n}
转化得 k m b n x ( m o d w n ) k&#x27;m\equiv b_n-x&#x27;\pmod{w_n} ,用扩展欧几里得求解。

这里又有一道题:
洗牌(NKOJ 4034)

问题描述
BH发明了一台洗牌机。机器上有一排共 N N 个格子,一开始,我们将编号 1 1 N N N N 张牌依次放入这 N N 个格子里。每个格子下方标识了一个数字,表示经过一次洗牌后,对应格子出现的牌的编号。
现在给你 N N 张牌和洗牌后要求得到的顺序,问洗牌机需要进行多少次洗牌才能得到指定牌序?如果无解,输出 1 -1

输入格式
第一行,一个整数 N N ( 1 N 520 1\leqslant N\leqslant520 )。
第二行, N N 个空格间隔的整数,表示洗牌机1到N每个格子下对应的标识。
第三行, N N 个空格间隔的整数,表示指定的牌序。

输出格式
一行,一个整数,表示所需洗牌次数。无解输出 1 -1

样例输入
4
2 3 4 1
3 4 1 2

样例输出
2

显然在不断洗牌的过程中,每个位置上牌的编号是循环出现的。
c [ i ] [ j ] c[i][j] i i 位置出现编号为 j j 的牌所需的最少洗牌次数(规定 c [ i ] [ i ] &gt; 0 c[i][i]&gt;0 ,则 c [ i ] [ i ] c[i][i] 表示 i i 位置牌的编号循环一次所需的次数), a [ i ] a[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&amp;\equiv c[1][a[1]]\pmod{c[1][1]}\\ x&amp;\equiv c[2][a[2]]\pmod{c[2][2]}\\ &amp;\vdots\\ x&amp;\equiv c[n][a[n]]\pmod{c[n][n]} \end{cases}

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;
}

猜你喜欢

转载自blog.csdn.net/PHenning/article/details/89041940