P4777 【模板】扩展中国剩余定理(EXCRT)/ poj2891 Strange Way to Express Integers

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

excrt模板

我们知道,crt无法处理模数不两两互质的情况

然鹅excrt可以

设当前解到第 i 个方程

设$M=\prod_{j=1}^{i-1}b[j]$ ,$ res$是前$ i-1 $个方程的最小解

则$ res+x*M$ 是前 $i-1 $个方程的通解

那么我们求的就是

$res+x*M ≡ a[i] (mod b[i])$

$<=> x*M - y*b[i] = a[i]-res$

用exgcd求出的解为 t (当且仅当 gcd(M,b[i])∣t 时有解)

x的一个解=$ t /gcd(M,b[i])*(a[i]-res)$

最小解=$ x\%( b[i] / gcd(M,b[i]) )$

∴$res=(res+x*M)\%( M=M*b[i] )$

如此递推

end.

poj2891 有多组数据,请自行修改(poj只能用 I64d 来着(大雾))

#include<cstdio>
#include<cctype>
using namespace std;
typedef long long ll;
char c;template<typename T>void read(T &x){
    c=getchar(); x=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); 
} 
ll mod(ll x,ll p) {return x<0 ?x+p:x;}
ll mul(ll x,ll y,ll p){
    ll tmp=x*y-(ll)((long double)x/p*y+1.0e-8)*p;
    return mod(tmp,p);
}ll g,a[100002],b[100002];
void exgcd(ll a0,ll b0,ll &x,ll &y){
    if(!b0) x=1,y=0,g=a0;
    else exgcd(b0,a0%b0,y,x),y-=x*(a0/b0);
}int n;
ll excrt(){
    ll res=a[1],M=b[1],x,y,c,t,B;
    for(int i=2;i<=n;++i){
        B=b[i];
        c=mod((a[i]-res)%B,B);
        exgcd(M,B,x,y); t=B/g;
        if(c%g) return -1;
        x=mul(x,c/g,t=B/g); 快速乘取模
        res+=x*M; M*=t;
        res=mod(res%M,M); 
    }return res;
}
int main(){
    read(n);
    for(int i=1;i<=n;++i) read(b[i]),read(a[i]);
    printf("%lld",excrt());
    return 0;
}

 

posted @ 2018-10-12 16:18  kafuuchino  阅读(205)  评论(0编辑  收藏  举报