/* 题意:physical、emotional和intellectual的周期分别为23、28和33天。现给出三个日期,p、e和i,分别对应physical、emotional和intellectual出现 峰值的日期。然后再给出一个日期d,求从d开始,经过多少天三个峰值会同时出现。 难度:*** 算法:扩展欧几里得算法+中国剩余定理 1.假设需要的天数为days,那么根据题意: (days+d)%23=p%23 (days+d)%28=e%28 (days+d)%33=i%33 2.令a、b、c分别为28*33模23、23*33模28、23*28模33的逆元,即: 28*33*a%23=1 23*33*b%28=1 23*28*c%33=1 求逆元可转化为求ax+by=gcd(a,b)的问题,当gcd(a,b)=1且x>0时,ax+by=1 ==> (ax+by)%b = ax%b=1,此时x为所求逆元。 逆元可由欧几里得扩展算法求解。 3.令 days+d = 28*33*a*(p%23) + 23*33*(e%28) + 23*28*(i%33),根据中国剩余定理,该值即为所求。 所以: days = 28*33*a*(p%23) + 23*33*(e%28) + 23*28*(i%33) - d 注意,由于p、e和i可能为0,此时days+d=lcm=23*8*33 */ #include <stdio.h> // 求ax+by=gcd(a,b)的一组特解 int exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1; y=0; return a; } int r=exgcd(b,a%b,x,y); int t=x; x=y; y=t-a/b*y; return r; } // 返回a关于b的逆元,即ax%b=1 int getInverse(int a,int b) { int x,y; int gcd = exgcd(a,b,x,y); if (gcd != 1) { return -1; } // x的通解是x=x0 + nb (n是任意正整数) // x%=b和x+=b本质都是对x加减b,所以计算后依然是ax+by=1的解。 // 这样做的目的,是将x固定在(0,b)之间,那么(ax+by)%b=ax%b=1,此时x为a模b的逆元。 x %= b; if (x < 0) { x += b; } return x; } int main() { int x,y; int a,b,c; int p,e,i,d; int lcm = 23*28*33; // 求逆元 a = getInverse(28*33,23); b = getInverse(23*33,28); c = getInverse(23*28,33); int cases=1; while (scanf("%d%d%d%d",&p,&e,&i,&d)) { if(p==-1 && e==-1 && i==-1 && d==-1) { break; } int peak = 28*33*a*(p%23)+23*33*b*(e%28)+23*28*c*(i%33); // 当p=e=i=0时,peak=0,此时应为lcm,因为两个相邻峰值之间得距离为lcm int days = (peak-d+lcm)%lcm; // 此处加lcm主要因为peak可能为0 if (days == 0) { days = 21252; } printf("Case %d: the next triple peak occurs in %d days.\n",cases++,days); } }