P4777 【模板】扩展中国剩余定理(EXCRT)

思路

中国剩余定理解决的是这样的问题
求x满足
\[ \begin{matrix}x \equiv a_1(mod\ m_1)\\x\equiv a_2(mod\ m_2)\\ \dots\\x\equiv a_n(mod\ m_n)\end{matrix} \]
在模数互质的情况下,解为
\[ x=\sum_ia_iM_iM_i^{-1}(mod M) \]
其中\(M=\prod_{i}m_i\)\(M_i=\frac{M}{m_i}\)\(M_i^{-1}\)\(M_i\)在模\(m_i\)意义下的逆元

在模数不互质的情况下,我们需要扩展中国剩余定理
设有两个同余方程
\[ x \equiv a_1(mod\ m_1)\\x\equiv a_2 (mod\ m_2) \]
其中\(m_1\)\(m_2\)不互质
可得到
\[ x=a_1+m_1x_1\\ x=a_2+m_2x_2 \]
所以得到
\[ a_1+m_1x_1 = a_2+m_2x_2 \]
变形后有
\[ m_1x_1+m_2x_2=a_2-a_1 \]
用exgcd解出最小的\(x_1\)
则有
\[ x \equiv (a_1+m_1x_1)(mod\ lcm(m_1,m_2)) \]
相当于把两个式子合并在一起,EXcrt就是将所有式子合并完即可

代码

因为不想写龟速乘所以用了__int128

#include <cstdio>
#include <algorithm>
#include <cstring>
#define int __int128
using namespace std;
int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    int req=exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return req;
}
int gcd(int a,int b){
    return (b==0)?a:gcd(b,a%b);
}
int lcm(int a,int b){
    return (a*b)/gcd(a,b);
}
int calc(int a,int b,int c){//ax+by=c
    int d=gcd(a,b);
    int x1,x2;
    exgcd(a,b,x1,x2);
    b/=d;
    x=(x*(c/d)%b+b)%b;
    return ans;
}
pair<int,int> merge(int a1,int m1,int a2,int m2){
    int x1=calc(m1,m2,a2-a1);
    return make_pair(x1*m1+a1,lcm(m1,m2));
}
int n,a[100100],m[100100];
signed main(){
    long long mx;
    scanf("%lld",&mx);
    n=mx;
    for(int i=1;i<=n;i++){
        scanf("%lld",&mx);
        m[i]=mx;
        scanf("%lld",&mx);
        a[i]=mx;
    }
    int mida=a[1],midm=m[1];
    for(int i=1;i<n;i++){
        pair<int,int> t= merge(mida,midm,a[i+1],m[i+1]);
        mida=t.first;
        midm=t.second;
    }
    printf("%lld\n",(long long)mida);
    return 0;
}

转载于:https://www.cnblogs.com/dreagonm/p/10514141.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值