思路
在信息学奥赛数论一本通里面我愣是没搞懂题解的5544、14421、1288这些数是怎么来的,在网上找了很多博客才搞懂中国剩余定理的原理。
总结一下步骤:(方程组形式为mod m意义下与a同余)求大模数M->对每个小模数M/m,求模m意义下的逆元i,那么M/mia就是满足方程的一个最小数,然后将每一个方程的最小数相加模M即可。两份代码上面是一本通的,下面是我理解之后手打AC的,希望方便各位理解。
代码
生肉版
//UVA756 Biorhythms #include<iostream> using namespace std; int main(){ int p,e,i,d,T=1; cin>>p>>e>>i>>d; do{ int lcm=21252; //lcm(23,28,33) int ans=(5544*p+14421*e+1288*i-d+lcm)%lcm; if(ans==0) ans=lcm; cout<<"Case "<<T++ <<": the next triple peak occurs in "<<ans<<" days."<<endl; cin>>p>>e>>i>>d; }while(p!=-1); return 0; }
解释版
#include<iostream> #define debug(x) cout<<"x="<<x<<endl #define int long long using namespace std; const int maxn=1e5+7; const int mod=1e9+7; typedef long long ll; //ifstream mycin("in.txt"); //ofstrem mycout("out.txt"); inline void read(int &data){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} data=x*f; } int n; int Exgcd(ll a,ll b,ll &x,ll &y){ //拓展欧几里得求逆元 if(!b){ x=1,y=0; return a; } int gcd=Exgcd(b,a%b,y,x); y-=a/b*x; } signed main(){ int p,e,i,d,T=1; int lcm,x,y,a,b,c,ans; lcm=23*28*33; //大模数 cin>>p>>e>>i>>d; Exgcd(28*33,23,a,y); //求三次逆元 Exgcd(23*33,28,b,y); Exgcd(23*28,33,c,y); do{ ans=(28*33*a*p%lcm+23*33*b*e%lcm+23*28*c*i%lcm-d)%lcm; //相加取模 ans=(ans+lcm-1)%lcm+1; //答案“不超过”lcm,要多加一步; cout<<"Case "<<T++ <<": the next triple peak occurs in "<<ans<<" days."<<endl; cin>>p>>e>>i>>d; }while(p!=-1); return 0; }