Advertisement

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

阅读量:

传送门

首先介绍拓展中国剩余定理。

\begin{cases} x\equiv a_1\pmod{p_1}\\ x\equiv a_2\pmod{p_2}\\ \dots\\ x\equiv a_n\pmod{p_n} \end{cases}

拓展中国剩余定理与传统版本的主要区别在于模数p_i是否两两互质。
我们需要计算前i-1项的最小公倍数M。
当加入新的同余式时,则有x + t*M \equiv a_i \pmod{p_i}
此时我们可以通过扩展欧几里得算法来求解t值。
进而得到新的x = x + t*M
问题要求我们找到n组非负整数a_i, b_i满足上述方程组,并且寻找满足条件的最小非负整数解x。
需要注意的是x的取值范围不超过1018,在实际计算过程中可能会超过long long范围,
因此我们需要采用快速乘法算法来解决溢出问题。

AC代码如下
复制代码
    /*---------------
    author:onebelieve
    ----------------*/
    #include<bits/stdc++.h>
    #define int long long
    #define rep(i,a,b) for(int i=a;i<b;i++)
    #define rip(i,a,b) for(int i=a;i<=b;i+)
    typedef long long ll;
    const int N=1e5+10;
    using namespace std;
    ll n,a[N],b[N];
    template<typename T> T quick(T a,T b,T p){T ans=1;while(b){if(b&1)ans=ans*a%p;a=a*a%p;b>>=1;}return ans;}
    template<typename T> T fmul(T a,T b,T p){T ans=0;while(b){if(b&1)ans=(ans+a)%p;a=a%p<<1%p;b>>=1;}return ans;}
    template<typename T> T gcd(T a,T b){return b==0?a:gcd(b,a%b);}
    template<typename T> T exgcd(T a,T b,T &x,T &y){
    	if(b==0){x=1,y=0;	return a;}
    	T Gcd=exgcd(b,a%b,x,y),temp=x;
    	x=y;	y=temp-a/b*x;	return Gcd;
    }
    
    inline ll excrt()
    {
    	ll M=b[0],ans=a[0],x,y;  //x代表我们要找的t
    	for(int i=1;i<n;i++)
    	{
    		ll a1=M,b1=b[i],c=(a[i]-ans%b1+b1)%b1;
    		ll gcd=exgcd<ll>(a1,b1,x,y),t=b1/gcd;  //此时找到的t只是假设a[i]-x=1时的解
    		if(c%gcd!=0)return -1;
    		x=fmul<ll>(x,c/gcd,t);  //此时的t才是我们要的
    		ans+=x*M;
    		M*=t;
    		ans=(ans%M+M)%M;
    	}
    	return (ans%M+M)%M;
    }
    
    signed main()
    {
    	cin>>n;
    	rep(i,0,n)cin>>b[i]>>a[i]; 
    	cout<<excrt()<<endl;
    return 0;
    }

全部评论 (0)

还没有任何评论哟~