中国剩余定理

来源:互联网 发布:拼多多商城源码 编辑:程序博客网 时间:2024/06/05 15:54

中国剩余定理:


类似于韩信点兵的问题。

求出方程 x = a[i](mod m[i]),mi互为素数

令Mi = m1*m2...*m[i-1]*m[i+1]*....

那么 gcd(Mi,mi) = 1.    故存在pi,qi使 Mi*pi + mi*qi = 1(扩展欧几里得)


令ei = Mi*pi

那么有  ei = 0(mod mj),j != i

       ei = 1(mod mj),j = i

所以:

e0*a0 + e1*a1 + ..... + en*an是方程的解,

在[ 0,m[0]*.....*m[n] )中只有唯一解,取模即可


poj 1006 模板题 

大意:

人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这天,人在对应的方面表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。


ll CRT(ll a[],ll m[],ll n){    ll M = 1;    for(ll i = 0; i < n; i++) M *= m[i];    ll ret = 0;    for(ll i = 0; i < n; i++)    {        ll x,y;        ll tm = M/m[i];        ex_gcd(tm,m[i],x,y);        ret = (ret+tm*x*a[i])%M;    }    return (ret+M)%M;}



poj 2891

求解mi不互素的问题,可以模仿中国剩余定理,两两合并

对于 x mod a0 = r0,可以知道最小值为 a0 + r0

若同时存在x mod a1 = r1 => (k0*a0 + r0)mod a1 = r1, 

那么 k1*a1 + r1 = k0*a0 + r0


于是得到 k0*a0 - k1*a1 = r1 - r0,利用扩展欧几里得可以求出x,公约数t

(但是前提是 a*m + b*n = gcd(a,b) ), 所以(r1 - r0)%t必需为0,否则不满足公式

于是我们可以得出k0 = x*(r1-r0)/t , x实际来自( a0*x + a1*y == gcd(a0,a1) )

所以这个公共的x1 = r0 + k0*x. 于是我们得到一个新的 a = x1  ,  r = x1 % lcm(a0,a1)


于是我们可以往后面慢慢推了。

假设有3对数: 8 4   :   7 3   : 12  7

x = 8*x0 + 4 = 7*x1 + 3   可以求出x为 11,于是把11作为r

a*t + r = 12*t1 + 7, 于是a要满足能同时除尽7和8,即它们的最小公倍数  /*个人见解


#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>using namespace std;typedef long long ll;typedef long double ld;const ld eps=1e-10;const int inf = 0x3f3f3f;const int maxn = 100005;ll ans,d;int flag;ll ex_gcd(ll a,ll b,ll &x,ll &y){    ll ret;    if(b == 0)    {        x = 1;        y = 0;        return a;    }    ret = ex_gcd(b,a%b,x,y);    ll tmp =x;    x = y;    y = tmp-(a/b)*y;    return ret;}ll get(ll m1,ll m2,ll a){    ll x,y;    d = ex_gcd(m1,m2,x,y);    if(a % d != 0)                            //无法满足等式    {        flag = 0;    }    ll t = (x*(a/d)%m2+m2)%m2;               //得出要求的x1    return t;}int main(){    ll m1,m2,a1,a2;    ll k;    while(scanf("%I64d",&k) != EOF)    {        flag = 1;        scanf("%I64d%I64d",&m1,&a1);        ans = a1;        for(ll i = 1; i < k; i++)        {            scanf("%I64d%I64d",&m2,&a2);            ll t = get(m1,m2,a2-a1);            ans += (m1*t);            m1 = m1*m2/d;                          //获得最小公倍数            ans = (ans%m1+m1)%m1;            a1 = ans;        }        if(!flag)            printf("-1\n");        else            printf("%I64d\n",ans);    }    return 0;}


hdu 1573

题意:

给你两组数a[],b[].让你求出≤n的正整数中,有多少个满足 x % a[i] = b[i]

感觉和上面那个差不多吧,合并后求出最小的值。 然后通过加所有数的最小公倍数得出结果


#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>using namespace std;typedef long long ll;typedef long double ld;const ld eps=1e-10;const int inf = 0x3f3f3f;const int maxn = 100005;ll a[15];ll b[15];ll ans,d;int flag;ll ex_gcd(ll a,ll b,ll &x,ll &y){    ll ret;    if(b == 0)    {        x = 1;        y = 0;        return a;    }    ret = ex_gcd(b,a%b,x,y);    ll tmp =x;    x = y;    y = tmp-(a/b)*y;    return ret;}ll get(ll m1,ll m2,ll a){    ll x,y;    d = ex_gcd(m1,m2,x,y);    if(a % d != 0)                            //无法满足等式    {        flag = 0;    }    ll t = (x*(a/d)%m2+m2)%m2;               //得出要求的x1    return t;}int main(){    ll m1,m2,a1,a2;    ll  n;    ll m;    int t;    scanf("%d",&t);    while(t--)    {        scanf("%I64d%I64d",&n,&m);        for(int i = 0; i < m; i++)            scanf("%d",&a[i]);        for(int j = 0; j < m; j++)            scanf("%d",&b[j]);        flag = 1;        ans = b[0];        m1 = a[0],a1 = b[0];        for(ll i = 1; i < m; i++)        {            m2 = a[i],a2 = b[i];            ll t = get(m1,m2,a2-a1);            ans += (m1*t);            m1 = m1*m2/d;                                      ans = (ans%m1+m1)%m1;            a1 = ans;        }        ll num = 0;        while(ans <= n)        {            if(ans != 0)                          //正整数                num ++;            ans += m1;        }        if(!flag)            printf("0\n");        else            printf("%I64d\n",num);    }    return 0;}


0 0