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

传送门


拓展中国剩余定理EXCRT

早知道有这东西就不学CRT了呜呜呜(ljCRT)
这个比CRT范围更广更快更好写……
虽然我写挂了,并且调了两天四五个小时
还是这一堆式子:

\[\begin{cases} x\equiv b_1\pmod {a_1}\\ x\equiv b_2\pmod {a_2}\\ x\equiv b_3\pmod {a_3}\\ \cdots\cdots\cdots\cdots\\ \end{cases}\]

但是 \(a_i\) 不需要互质了。
首先考虑通解:
假设我们已经求出了某个同余方程的一个特解 \(x\),那么通解就等于 \(x+ka\)
于是我们就有了一个思路——不断合并同余方程。
那么问题来了,怎么合并两个同余方程式?
先求出其中一个的通解,设为 \(ans+xa_1\),则有:

\[ans+xa_1\equiv b_2\pmod {a_2} \]

移项得:

\[xa_1\equiv b_2-ans\pmod {a_2} \]

这个就可以转化成:

\[xa_1+ya_2=b_2-ans \]

再使用 \(exgcd\) 求得 \(x\) 的一个解,新的特解就等于 \(ans+xa_1\)
接下来就把这个特解继续带入下一个同余方程式,一步步计算即可。
注意要使用龟速乘。

注意事项

主要问题在龟速乘上,请看龟速乘

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100005;
int n;
long long m,x,y,ans;
long long a[maxn],b[maxn];
inline long long ksc(long long a,long long b,long long mod){
    return (a*b-(long long)((long double)a/mod*b)*mod+mod)%mod;
}
long long exgcd(long long a,long long b){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    long long gcd=exgcd(b,a%b);
    swap(x,y);
    y=y-(a/b)*x;
    return gcd;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
    m=a[1],ans=b[1]%a[1];
    for(int i=2;i<=n;i++){
        long long gcd=exgcd(m%a[i],a[i]);
        long long c=b[i]-ans;
        x=ksc(x,c/gcd,a[i]);
        long long M=m/gcd*a[i];
        ans=((ans+x*m)%M+M)%M;
        m=M;
    }
    cout<<(ans%m+m)%m;
    return 0;
}
posted @ 2021-06-11 22:58  尹昱钦  阅读(54)  评论(0编辑  收藏  举报