扩展CRT

CRT是用于解一组同余方程:

$ x ≡ c1 ( mod\ m1)$

$ x ≡ c2 ( mod\ m2)$

...

$ x ≡ cn ( mod\ mn)$

当模数两两互质的时候,显然可以直接用朴素CRT合并

那当模数不互质的时候,就需要推一波式子采用扩展CRT了

考虑合并两个方程:

$ x ≡ c1 ( mod\ m1)$

$ x ≡ c2 ( mod\ m2)$

显然有$ x = c1+k1m1$, $ x = c2+k2m2$, 即$c1+k1m1=c2+k2m2$

移项得$ k1m1=k2m2+c2-c1$

我们令$ t=gcd(m1,m2)$, 则有$ \frac{m1}{t}k1=\frac{m2}{t}k2+\frac{c2-c1}{t}$

显然当且仅当$ t|c2-c1$时可以合并

此时有$ \frac{m1}{t}k1≡\frac{c2-c1}{t} (mod \frac{m2}{t})$

由于$ k1$的系数$ \frac{m1}{t}$和模数$ \frac{m2}{t}$互质,通过扩展欧几里得求得逆元$ inv$

则化简得$ k1≡\frac{c2-c1}{t}*inv \ (mod \frac{m2}{t})$

将$ k1$代回第一个方程得$ x≡(\frac{c2-c1}{t}*inv \ mod \frac{m2}{t})*m1+c1(mod \frac{m1*m2}{t})$

这是一个同余方程的形式,如此不断合并所有方程即可

时间复杂度:$ O$(方程数*$ log$(值域))

code:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rt register int
#define l putchar('\n')
#define ll long long
#define r read()
using namespace std;
inline ll read(){
    register ll x = 0; char zf = 1; char ch;
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int i,j,k,m,n,x,y,z,cnt,sum;
struct calc{
    ll c,m;
}ans,now;
inline ll mul(ll x,ll y,ll mod){
    ll tmp=(x*y-(ll)((long double)x/mod*y+1e-8)*mod);
    return tmp<0?tmp+mod:tmp;
}
ll gcd(ll x,ll y){return !y?x:gcd(y,x%y);}
void exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){x=1;y=0;return;}
    exgcd(b,a%b,y,x);y-=a/b*x;
}
ll inv(ll A,ll p){
    ll x,y;
    exgcd(A,p,x,y);
    return (x<=0)?(x+p):x;
}
calc operator &=(calc &x,calc y){
    const ll t=gcd(x.m,y.m);
    if(abs(y.c-x.c)%t){cout<<-1;exit(0);}
    x.c=mul(mul((y.c-x.c)/t,inv(x.m/t,y.m/t),y.m),x.m,x.m/t*y.m)+x.c;
    x.m=x.m/t*y.m;while(x.c<0)x.c+=x.m;
}
int main(){
    n=read();
    ans.m=read();ans.c=read();
    for(rt i=1;i<n;i++){
        now.m=read();now.c=read();
        ans&=now;
    } 
    while(ans.c<0)ans.c+=ans.m;
    cout<<ans.c;
    return 0;
}

 

posted @ 2018-07-24 10:46  Kananix  阅读(687)  评论(3编辑  收藏  举报

Contact with me