(模板)中国剩余定理 重学笔记 POJ1006

资料推荐

有很大帮助的博客
——(本文多处引用其中金句,特此注明出处并鞠躬感谢博主。


主要定理

  • 定理一: 几个数相加,如果任何一个数不能被div整除,那么这几个数的和一定不能被div整除。
  • 定理二: 两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)。

主要步骤

  • 求出各个除数(div)的最小公倍数(lcm)。
  • 求出各个除数的基础数(base),某个除数的基础数应该满足几个性质:1. 能被除了这个除数以外的所有除数整除,2.除以这个除数的余数符合题意。
  • 将所有基础数求和后取模最小公倍数,结果即为答案。

相关证明

上面的那个博客非常的经典,在此直接引用并深鞠躬感谢博主。

(1)最小公倍数就不用解释了,跳过(记住,这里讨论的都是两两互质的情况)

(2)观察求每个数对应的基础数时候的步骤,比如第一个。105÷3=35。显然这个35是除了当前这个数不能整除以外都能够被其他数整除,就是其他数的最小公倍数。相当于找到了最小的开始值,用它去除以3发现正好余2。那么这个基础数就是35。记住35的特征,可以整除其他数但是不能被3整除,并且余数是2。体现的还不够明显,再看下5对应的基础数。21是其他数的最小公倍数,但是不能被5整除,用21除以5得到的余数是1,而要求的数除以5应该是余1的。所以余数被扩大,就得到了相应的基础数63。记住这个数的特征,可以被其他数整除但是被5除应该余三。同理,我们得到了第三个基础数23,那么他的特征就是:可以被其他数整除,但是不能被7整除,并且余数为2。

(3)第三步基础数加和,为什么要这样做呢?利用就是上面提到的定理1。
35+63+30=128。对于3来说,可以把63+30的和看作一个整体,应该他们都可以被3整除。看着上面写出的三个数的特征,运用定理1来说,就是在35的基础上加上一个可以被3整除的倍数,那么得到的结果依然还是满足原先的性质的,就是128除以同样还是余2的。同理,对于5还说,这个数被除之后会剩余3;对于7来说,被除之后剩余2。所以说,我们当前得到的这个数是满足题目要求的一个数。但是这个数是不是最小的,那就不一定了。

(4)应该不能确定是不是最小的数,这个时候就要用到他们的最小公倍数了。最小公倍数顾名思义,一定是一个同时被几个数整除的最小的一个数,所以减去它剩余下来的余数还是符合题意要求的。当然也同样可以运用定理1来解释,只不过是加法变成了减法,道理还是一样的。当然具体要不要剪还是要看和lcm的大小关系的。


我的代码

#include <cstdio>
#include <iostream>
using namespace std;

typedef long long ll;
const int maxn=int(20);
int n;
ll div[maxn],r[maxn];

long long exgcd(ll a,ll b,ll &x,ll &y) {
    if(b==0) {x=1,y=0; return a;}
    ll res=exgcd(b,a%b,x,y), tmp=y;
    y=x-y*(a/b), x=tmp;
    return res;
}

ll china(ll n,ll *div,ll *r) {
    ll lcm=1,d,y,x=0;
    for(int i=1;i<=n;i++)
        lcm*=div[i];
    for(int i=1;i<=n;i++) {
        ll base=lcm/div[i];
        exgcd(div[i],base,d,y);
        x=(x+y*base*r[i])%lcm;
    }
    return (x+lcm)%lcm;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&div[i],&r[i]);

    printf("%lld\n",china(n,div,r));

    return 0;
}

例题 POJ1006

题目描述

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

题解

由题意可知,设满足条件的那天是第res天,每个生理周期的周期长度分别为T1,T2,T3,每个峰值出现的日期分别为D1,D2,D3,则满足以下等式: res=T1k+D1 res=T2k+D2 res=T3k+D3 ,其中k在各式中可以不相同且是任意整数;
由此可以列出同余线性方程组,用中国剩余定理求解即可。

代码

#include <cstdio>
#include <iostream>
using namespace std;

typedef long long ll;
long long exgcd(ll a,ll b,ll &x,ll &y) {
    if(b==0) {
        x=1, y=0;
        return a;
    } else {
        long long res=exgcd(b,a%b,x,y), tmp=y;
        y=x-y*(a/b), x=tmp;
        return res;
    }
}

long long China(int n,ll *div,ll *r) {
    long long lcm=1,x=0;
    for(int i=1;i<=n;i++)
        lcm*=div[i];
    for(int i=1;i<=n;i++) {
        long long base=lcm/div[i],tmp,cur;
        exgcd(div[i],base,tmp,cur);
        x=(x+cur*base*r[i])%lcm;
    }
    return (x+lcm)%lcm;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE

    long long div[5]={0,23,28,33},r[5]={};
    int cur,n=3,cas=0;

    while(scanf("%lld%lld%lld%d",&r[1],&r[2],&r[3],&cur)==4 && ~cur) {
        for(int i=1;i<=n;i++)
            r[i]%=div[i];
        long long res=China(n,div,r)-cur;
        if(res<=0) res+=21252;
        if(res>21252) res-=21252;
        printf("Case %d: the next triple peak occurs in %lld days.\n",++cas,res);
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值