zoukankan      html  css  js  c++  java
  • P1495 CRT,P4777 EXCRT

    updata on 2020.4.11
    修正了 excrt 的一处笔误

    CRT

    求解方程:

    \[\begin{cases} x \equiv a_1 \pmod {m_1}\\ x \equiv a_2 \pmod {m_2}\\ \vdots \\ x \equiv a_n \pmod {m_n}\\ \end{cases} \]

    其中,保证\(m_i\)是两两互质的正整数,crt就是基于这个特征
    我们记\(M=\prod_{i=1}^{n} m_i\),和\(M_i=\dfrac{M}{m_i}\)\(t_i\)满足\(M_it_i \equiv 1 \pmod {m_i}\)
    \(M_i\)是所有下标不为\(i\)\(m\)乘起来,而\(t_i\)\(M_i \bmod m_i\)的逆元
    则对于任意的\(k\neq i\)\(a_iM_it_i\equiv 0\pmod {m_k}\),因为\(M_i\)中一定包含了\(m_k\)这个因数
    而又因为\(M_it_i\equiv1\pmod{m_i}\),所以\(a_iM_it_i\equiv a_i\pmod{m_i}\)
    所以可以说明

    \[\sum_{i=1}^n a_iM_it_i \]

    为问题的一组解,且这个解在\(\mod M\)下唯一
    当然通解就是\(x+kM,k\subset Z\)
    当然求逆元的过程要用到exgcd,而题目要求最小正整数解,所以只要让解在\([0,M-1]\)中就行了
    但是,如果直接\(M=\prod_{i=1}^n m_i\)会乘爆,要让\(M=\text{LCM}_{i=1}^n m_i\),可以有和上文叙述的一样的性质,下面的excrt也是一样
    题目代码

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	int x=0,y=1;
    	char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    int n;
    LL a[15],m[15],M=1;
    void exgcd(LL a,LL b,LL &x,LL &y){
    	if(!b){
    		x=1;y=0;
    		return;
    	}
    	exgcd(b,a%b,x,y);
    	LL tmp=x;x=y;
    	y=tmp-a/b*y;
    }
    int main(){
    	n=read();
    	for(reg int i=1;i<=n;i++){
    		m[i]=read();a[i]=read();
    		M=M/std::__gcd(M,m[i])*m[i];
    	}
    	LL ans=0,Mi,x,y;
    	for(reg int i=1;i<=n;i++){
    		Mi=M/m[i];
    		exgcd(Mi,m[i],x,y);
    		ans=((ans+Mi*x*a[i])%M+M)%M;
    	}
    	std::printf("%lld",ans);
    	return 0;
    }
    

    EXCRT

    还是求解方程:

    \[\begin{cases} x \equiv a_1 \pmod {m_1}\\ x \equiv a_2 \pmod {m_2}\\ \vdots \\ x \equiv a_n \pmod {m_n}\\ \end{cases} \]

    但这次不保证\(m_i\)两两互质
    不过这好像和CRT关系不大
    考虑用数学归纳法,假设我们已知前\(i-1\)的解为\(ans\),并记\(M=\text{LCM}_{k=1}^{i-1}m_k\) 则其通解为\(ans+M\times t\)

    那么,我们就要确定一个\(t\),使得\(ans+M\times t\equiv a_i\pmod {m_i}\)
    然后新的\(ans=ans+M\times t\)
    考虑求解上面那个同余方程的方法
    因为exgcd可以求解方程\(ax+by=\gcd(a,b)\)
    那么我们转变那个同余方程的形式:

    \[Mt\equiv a_i-ans \pmod{m_i} \]

    \[Mt+m_iy=a_i-ans \]

    那么如果\(\gcd(M,m_i)|(a_i-ans)\),则有解,题目保证了有解
    用exgcd求出的是:

    \[Mt'+m_iy=\gcd(M,m_i) \]

    那么让等式两边同时除以这个\(\gcd\)再同时乘以\(a_i-ans\)就行了

    \[Mt'\dfrac{(a_i-ans)}{\gcd(M,m_i)}+m_iy\dfrac{(a_i-ans)}{\gcd(M,m_i)}=a_i-ans \]

    那我们要求的这个\(t\)就是\(t'\dfrac{(a_i-ans)}{\gcd(M,m_i)}\)
    注意在乘的时候会爆long long,要用int128或是龟速乘
    我才不会告诉你们我快读不开long long见祖宗了
    还有就是要通过\(ans=(ans+M)\bmod M\)来保证\(ans\)是正的
    上代码,因为不开long long那事调了好长时间。。

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

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline LL read(){
    	LL x=0,y=1;
    	char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    int n;
    LL a[100006],m[100006];
    inline LL mul(LL n,LL k,LL mod){
        LL ans=0;
        while(k){
          if(k&1) ans=(ans+n)%mod;
          k>>=1;
          n=(n+n)%mod;
        }
        return ans;
    }
    LL exgcd(LL a,LL b,LL &x,LL &y){
        if(!b){x=1;y=0;return a;}
        LL ret=exgcd(b,a%b,x,y);
        LL z=x;x=y;y=z-(a/b)*y;
        return ret;
    }
    inline LL excrt(){
    	LL x,y;
    	LL M=m[1],ans=a[1];
    	for(reg int i=2;i<=n;i++){
    		LL b=((a[i]-ans)%m[i]+m[i])%m[i];
    		LL gcd=exgcd(M,m[i],x,y);
    		x=mul(x,b/gcd,m[i]);
    		ans+=M*x;
    		M*=m[i]/gcd;
    		ans=(ans+M)%M;
    	}
    	return ans;
    }
    int main(){
    	n=read();
    	for(reg int i=1;i<=n;i++) m[i]=read(),a[i]=read();
    	std::printf("%lld",excrt());
    	return 0;
    }
    

     
    话说去年暑假在洛谷网校就学过一遍crt和excrt了
    但当时就没怎么理解清楚,更写不出代码
    这次是因为扩展卢卡斯要用到crt,才来写了一遍这两个题。。。

  • 相关阅读:
    IOS-- UIView中的坐标转换
    iphone练习之手势识别(双击、捏、旋转、拖动、划动、长按)UITapGestureRecognizer
    Storm与Spark Streaming比较
    Python程序的常见错误(收集篇)
    Python画图笔记
    如何在论文中画出漂亮的插图?
    别老扯什么Hadoop了,你的数据根本不够大
    保险与互联网结合拉开序幕
    关于数学
    R--基本统计分析方法(包及函数)
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12527101.html
Copyright © 2011-2022 走看看