中国剩余定理&拓展中国剩余定理

复习

中国剩余定理:

x ≡ a 1 ( m o d m 1 ) x ≡a_1(mod m_1) xa1(modm1)
x ≡ a 2 ( m o d m 2 ) x ≡a_2(mod m_2) xa2(modm2)
x ≡ a 3 ( m o d m 3 ) x ≡a_3(mod m_3) xa3(modm3)

x ≡ ( a n m o d m n ) x ≡(a_nmod m_n) x(anmodmn)
有整数解 x= ∑ n i = 1 a i M i t i \sum_{n}^{i=1}a_iM_it_i ni=1aiMiti
证明:因为 M i = m / m i M_i=m/m_i Mi=m/mi,是除了 m i m_i mi之外的所有模数的倍数,所以除了第i组和他进行同余的答案都是0。又因为 a i M i t i ≡ a i ( m o d m i ) a_iM_it_i≡a_i(mod m_i) aiMitiai(modmi)带入 x = ∑ n i = 1 a i M i t i x=\sum_{n}^{i=1}a_iM_it_i x=ni=1aiMiti。成立
中国剩余定理给出了模数两两互质的现行同余方程组的一个特解。方程组的通解可以表示成 x + k m x+km x+km。有些题目要求我们求非负解。只需要把x对m取膜就可以了。

例题:

自从曹冲搞定了大象以后,曹操就开始琢磨让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲很不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。

举个例子,假如有 16 头母猪,如果建了 3 个猪圈,剩下 1 头猪就没有地方安家了;如果建造了 5 个猪圈,但是仍然有 1 头猪没有地方去;如果建造了 7 个猪圈,还有 2 头没有地方去。

你作为曹总的私人秘书理所当然要将准确的猪数报给曹总,你该怎么办?

输入格式
第一行包含一个整数 n,表示建立猪圈的次数;

接下来 n 行,每行两个整数 ai,bi,表示建立了 ai 个猪圈,有 bi 头猪没有去处。

你可以假定 ai,aj 互质。

输出格式
输出仅包含一个正整数,即为曹冲至少养猪的数目。

数据范围
1 ≤ n ≤ 10 1≤n≤10 1n10,
1 ≤ b i ≤ a i ≤ 1000 1≤b_i≤a_i≤1000 1biai1000
所 有 a i 的 乘 积 不 超 过 1 0 18 所有ai的乘积不超过 10^{18} ai1018
输入样例:
3
3 1
5 1
7 2
输出样例:
16
感觉crt好多都要用龟速乘

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int a[N],b[N];
int exgcd(int a,int b,int &x,int &y)
{
    if(!b){
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

signed main()
{
    int n;
    cin>>n;
    int M=1;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        M*=a[i];
    }
    int res=0;
    for(int i=1;i<=n;i++){
        int mi=M/a[i];
        int ti,y;
        exgcd(mi,a[i],ti,y);
        res+=b[i]*mi*ti;
    }
    cout<<(res%M+M)%M<<endl;
}

拓展中国剩余定理

当我们mod的数不满足互质的时候,就不能用crt了,需要用excrt。具体操作及证明如下:
假设已经求出了钱k-1个方程构成的方程组解x,记 m = ∏ 1 k − 1 m i m=\prod _{1}^{k-1}m_i m=1k1mi
则我们 x + m ∗ i x+m*i x+mi为一组通解。
现在我们考虑加入第k组方程。求得一个t使得 x + t ∗ m ≡ a k ( m o d m k ) x+t*m≡a_k(modm_k) x+tmak(modmk)化简得到
t ∗ m ≡ a k − x ( m o d m k ) t*m≡a_k-x(modm_k) tmakx(modmk)这个时候我们就可以用拓展欧几里得来算出我们的答案有没有解,有解的话我们 x 1 = t ∗ m + x x_1=t*m+x x1=tm+x。所以excrt其实就是一组一组的使用crt来判断并求解。
板子题传送门

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+7;
int a[N],b[N],n;\
int mul(int a,int b,int mod)
{
    int res=0;
    while(b){
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
int exgcd(int a,int b,int &x,int &y)
{
    if(!b){
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int excrt()
{
    int x,y,k;
    int M=b[1],res=a[1];
    for(int i=2;i<=n;i++){
        int A=M,B=b[i],c=((a[i]-res)%B+B)%B;
        int gcd=exgcd(A,B,x,y),bg=B/gcd;
        if(c%gcd) return -1;
        x=mul(x,c/gcd,bg);
        res+=x*M;
        M*=bg;
        res=(res%M+M)%M;
    }
    return (res%M+M)%M;
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",b+i,a+i);
    }
    printf("%lld",excrt());
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值