资料推荐
有很大帮助的博客
——(本文多处引用其中金句,特此注明出处并鞠躬感谢博主。)
主要定理
- 定理一: 几个数相加,如果任何一个数不能被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=T1∗k+D1
,
res=T2∗k+D2
,
res=T3∗k+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;
}