【题意】对于C的for(i=A ; i!=B ;i +=C)循环语句,问在k位存储系统中循环几次才会结束。若在有限次内结束,则输出循环次数。否则输出FOREVER。
【解题思路】
题意不难理解,只是利用了 k位存储系统 的数据特性进行循环。例如int型是16位的,那么int能保存2^16个数据,即最大数为65535(本题默认为无符号),当循环使得i超过65535时,则i会返回0重新开始计数,如i=65534,当i+=3时,i=1,其实就是 i=(65534+3)%(2^16)=1。有了这些思想,设对于某组数据要循环x次结束,那么本题就很容易得到方程: x=[(B-A+2^k)%2^k] /C,即 Cx=(B-A)(mod 2^k) 此方程为 模线性方程,本题就是求X的值。
下面将结合红书进行简述,因此先把上面的方程变形,统一符号。令a=C ,b=B-A ,n=2^k,那么原模线性方程变形为:ax=b (mod n),该方程有解的充要条件为 gcd(a,n) | b ,即 b% gcd(a,n)==0,令d=gcd(a,n),有该方程的 最小整数解为 x = e (mod n/d),其中e = [x0 mod(n/d) + n/d] mod (n/d) ,x0为方程的最小解,那么原题就是要计算b% gcd(a,n)是否为0,若为0则计算最小整数解,否则输出FOREVER。剩下的就是套单变元模线性方程的板子了。
【AC代码】
#include <stdio.h> typedef long long LL; LL ex_gcd(LL a,LL b,LL &x,LL &y) { if(b==0) { x=1,y=0; return a; }else{ LL r = ex_gcd(b,a%b,y,x); y-=x*(a/b); return r; } } int main() { int A,B,C,k; while(scanf("%d%d%d%d",&A,&B,&C,&k)!=EOF) { if(A==0&&B==0&&C==0&&k==0)return 0; LL a = (LL)C; LL b = (LL)(B-A); LL n = 1LL<<k; LL x,y; int d = ex_gcd(a,n,x,y); if(b%d!=0) { puts("FOREVER"); } else { x = (x*(b/d))%n; //ax = b(mod n)最小解 x = ((x%(n/d)+n/d))%(n/d);//ax = b(mod n)最小整数解 printf("%I64d\n",x); } } return 0; }
【补充】单变元模线性方程板子
[说明]d = ex_gcd(a,n),先使用ex_gcd()求ax+ny=d的解,如果b不能整除d则无解,否则mod n意义下得解有d个,可以通过对某个解不断的加n/d得到。 typedef long long LL; vector<LL>line_mod_equation(LL a,LL b,LL n) { LL x,y; LL d = ex_gcd(a,n,x,y); vector<LL>ans; ans.clear(); if(b%d!=0) { x%=n,x+=n,x%=n; ans.push_back(x*(b/d)%(n/d)); for(LL i=1; i<d; i++)ans.push_back((ans[0]+i*n/d)%n); } return ans; }