CRT&&EXCRT&&EXGCD

[题目链接] (https://ac.nowcoder.com/acm/contest/890/D)
为了切一道板子题,特意去学习了一下扩展中国剩余定理。
首先讲讲CRT :先了解两个定理

  1. 若 a % b = c , 则 ( a + k * b ) % b = c ( k 为非负整数)。 这个定理很好理解不证明了。
  2. 若 a % b = c , 则 ( k * a ) % b = k * c (k为非负整数,这是在mod b 的环境下的) 。
    证明 : ( k * a ) % b = ( a * a * a … *a ) % b = c + c + … + c = k * c ;证毕。
    举个例子 : 求一个数x , x % m1 = c1 , x % m2 = c2 , x % m3 = c3 ;
    剩余定理的思想就是 : 令 x1 = k1 * m1 + c1 , x2 = k2 * m2 + c2 , x3 = k3 * m3 + c3;
    最后 x = ( x1 + x2 + x3 ) % lcm(m1,m2,m3);
    所以要使得 x1 是 lcm(x2,x3); 同理 x2 = lcm(x1,x3) , x3 = lcm (x1,x2);
    原因是 定理1 。
    然后 x1 还要满足 mod m1 = c1 , 方法是先找一个数 xx , 使得 xx % m1 = 1 (在mode m1环境下) , 那么 x1 * xx mod m1 = c1 ; 证明由定理二可得。
    数学求解:
    在这里插入图片描述
    注意:使用中国剩余定理的条件是m1,m2…mn互质。
    解法 设M = m1 * m2 * … * mn ; 设 Mi = M / mi , i = [1,n] ; 设 ti 为在mod mi 下Mi 的逆元。
    Mi * ti = 1 (mod mi) , i = [1,n] ; x = a1 * t1 * M1 + a2 * t2 * M2 + … + an * tn * Mn ;
    在这里插入图片描述
    这里不给代码了,到EXCRT有代码,因为EXCRT应用的更多。
    EXCRT 就是没有 m1,m2,…,mn必须互质的条件下求x。
    x≡c1(modm1) ;
    x≡c2(modm2) ;
    比如这两个方程的求解:
    x = k1 * m1 + c1 , x = k2 * m2 + c2 ;
    由已知 :k1 * m1 + c1 = k2 * m2 + c2 ;
    m1 * k1 = k2 * m2 + c2 - c1 ;
    若该方程存在解,则有(m1,m2)|(c2−c1),否则无解。
    这句话什么意思呢? 去学习一下exgcd吧 。
    exgcd 的应用就是 a * x + b * y = gcd ( a , b ) ;
    这个方程一定有解。 那么对于一般方程 a * x + b * y = c 判断它是否有解 , 只需要判断
    c 能否整除 gcd( a , b ); 如果能够整除那么就一定有解,否则无解。
    如果你看证明不是很懂,就只要知道exgcd的用法:
    exgcd说白就是解a * x + b * y = c 这种形式方程的一组解。
    那么证明一下:
    对于ax+by=gcd(a,b)
    因为gcd(a,b)=gcd(b,a%b)
    ax+by=gcd(a,b)
    bx1+(a%b)y1=gcd(b,a%b)
    可以变成:ax+by=bx1+(a%b)y1
    就是:ax+by=bx1+(a-[a/b]*b)y1
    所以:ax+by=ay1+b(x1-[a/b]*y1)
    所以,如果求出来y1,x1,就可以对应的求出来x,y
    x=y1,y=(x1-[a/b]*y1)
    不停地递归下去。
    边界:b==0 时,ax+by=gcd(a,b)
    这时候,b是0,a就是gcd(a,b),所以x=1,y随便一个值,都是一个特解。
void exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=0;return;
    }
    exgcd(b,a%b,y,x);
    y-=(a/b)*x;
}

让我们回到EXCRT
下面令d = ( m1 , m2 )。
我们对等式两边全部除以d,得:
m1 / d * k1 = ( c2−c1 ) / d+ m2 / d * k2 ;
经过简单变式,得:
m1 / d * k1 ≡ (c2−c1) / d ( mod m2 / d )
没错,我们成功消掉了k2
我们将m1 / d移项到等式右侧,得:
k1≡inv(m1 / d, m2 / d ) × ( c2 − c1 ) / d ( mod m2 / d ) ;
其中 inv(x,y) 表示模y意义下x的乘法逆元
重新将该式子变回等式,得:
k1 = inv ( m1 / d , m2 / d ) ×( c2 − c1 ) / d + y * ( m2 / d ) ;
该式子已经化简到尽了,考虑重新代入回最初的式子。
将k1代入x=c1+m1 * k中,得:
x ≡ inv ( m1 / d,m2 / d ) × ( c2 − c1 ) / d × m1 + c1 ( mod (m1*m2) / d) ;
至此,求两条式子的扩展CRT已经讲完了。
如果方程有多条怎么办:我们做n−1次的两条式子的CRT合并就可以了。
[讲解参考] (https://www.cnblogs.com/xiefengze1/p/10350652.html);

最后 题解 :

#include<bits/stdc++.h>
using namespace std;
#define I __int128
I read(){long long x;scanf("%lld",&x);return x;}
void exgcd(I a,I b,I&x,I&y){ //  assert(__gcd(a,b)==c)
    if(b==0) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}
  
bool merge(I x1,I p1,I x2,I p2,I&x,I&p){
    I u,v,d=__gcd(p1,p2);
    if((x2-x1)%d!=0) return false;
    exgcd(p1,p2,u,v);
    u = u * ( (x2-x1) / d );
    p=p1/d*p2;    //lcm
    I n=p2/d;
    u= ((u%n)+n)%n;
    x= u*p1 + x1;
    return true;
}
  
int main(){
    I n=read(),m=read();
    I x=0,p=1,flag=0;
    for(I i=0;i<n;i++){
        I p1=read(),x1=read();
        if(flag==2) continue;
        if(!merge(x,p,x1,p1,x,p)) flag=2;
        else if(x>m) flag=1;
    }
    if(flag==0) printf("%lld",x);
    else if(flag==1) puts("he was probably lying");
    else puts("he was definitely lying");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值