中国剩余定理

中国剩余定理:


类似于韩信点兵的问题。

求出方程 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
");
        else
            printf("%I64d
",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
");
        else
            printf("%I64d
",num);
    }
    return 0;
}

  



原文地址:https://www.cnblogs.com/Przz/p/5409653.html