中国剩余定理(crt)和扩展中国剩余定理(excrt)

中国剩余定理(crt)

crt是用来对于一个一元线性同余方程求解的算法:

 其中 m 1,m2​,m3​...mk​为两两互质的整数
定理:

我们设  M= \prod_{i=1}^{n}m_{i}  ,设   M_{i}=M/m  设  t_{i} = M_{i}^{-1}为  M_{i}m_{i} 意义下的逆元.

这个一元线性同余方程组的通解为:

x=\left ( \sum_{i=1}^{n}a_{i}t_{i}M_{i} \right )mod M

证明(来自百度百科):

下面上板子题:

【模板】中国剩余定理(CRT)/ 曹冲养猪 - 洛谷https://www.luogu.com.cn/problem/P1495

#include<bits/stdc++.h>
#define int long long
int a[20],b[20];
void exgcd(int a,int b,int& x,int& y)
{
	if(!b)
    {
		x=1,y=0;
		return;
	}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
int inv(int a,int b)
{
    int x,y;
    exgcd(a,b,x,y);
    return (x%b+b)%b;
}
int ksc(int a,int b,int p)
{
    int res=0;
    while(b)
    {
        if(b&1)
            res=(res+a)%p;
        a=(a+a)%p;
        b>>=1;
    }
    return res;
}
void solve()
{
    int n,M=1,ans=0;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i],&b[i]);
        M=M*a[i];
    }
    for(int i=1;i<=n;i++)
    {
        int m=M/a[i];
        int invm=inv(m,a[i]);
        ans=(ans+ksc(ksc(invm,M/a[i],M),b[i],M))%M;
    }
    printf("%lld\n",ans);
    return ;
}
signed main()
{
    solve();
    return 0;
}

扩展中国剩余定理(excrt)

拓展crt的作用也是求解下列方程组,但是约束条件不同:

 其中 m 1,m2​,m3​...mk​为不一定两两互质的整数.

我们每次选出两个方程利用exgcd进行合并即可,具体推算过程如下:

依照上面的证明两两结合,最后把全部结合即可.

证明完毕,来板子题耍耍:

【模板】扩展中国剩余定理(EXCRT) - 洛谷https://www.luogu.com.cn/problem/P4777

#include<bits/stdc++.h>
#define int long long
using namespace std;
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 mul(int a,int b,int mod)
{
    int res=0;
    while(b>0)
    {
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}

void solve()
{
    int n;
    scanf("%lld",&n);
    int a1,m1,a2,m2;
    scanf("%lld%lld",&m1,&a1);
    for(int i=2;i<=n;i++)
    {
        scanf("%lld%lld",&m2,&a2);
        int k1,k2;
        int d=exgcd(m1,m2,k1,k2);
        int c=((a2-a1)%m2+m2)%m2;
        if(c%d)
        {
            /*无解*/   
        }
        k1=mul(k1,c/d,m2/d);
		a1=a1+k1*m1;
		m1=abs(m1/d*m2);
    }
    printf("%lld\n",(a1%m1+m1)%m1);
    return ;
}
signed main()
{
    solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值