Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 23143 | Accepted: 6357 |
Description
for (variable = A; variable != B; variable += C)
statement;
I.e., a loop which starts by setting variable (变量的) to value A and while variable is not equal to B, repeats statement (声明) followed by increasing the variable by C. We want to know how many times does the statement get executed (实行) for particular values of A, B and C, assuming (承担) that all arithmetics is calculated (计算) in a k-bit unsigned integer (整数) type (with values 0 <= x < 2 k) modulo 2 k.
Input
The input is finished by a line containing four zeros.
Output
Sample Input
3 3 2 16 3 7 2 16 7 3 2 16 3 4 2 16 0 0 0 0
Sample Output
0 2 32766 FOREVER
题意:告诉你一个循环for(i=a;i!=b;i+=c)问你在2^k方内至少循环多少次才能退出,如果不能退出就输出“FOREVER”,也就是说i不断累加的过程中需要mod2^k。
输入:a,b,c,k我们要解的就是a+cx=b(modk)(下面的2^k我都用k来代替)。
要解这个x我们先来看一下扩展欧几里得算法:
书本上的内容:先用一个引例
直线上的点。求直线ax+by+c=0上有多少个整数点(x,y)满足x属于[x1,x2],y属于[y1,y2]。
下面是扩展欧几里得算法的程序:
void exgcd(int a,int b,int &d,int &x,int &y)
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
exgcd(b,a%b,d,y,x);
y-=a/b*x;
}
}
具体的证明方法可以自行百度,d最后的结果就是gcd(a,b)。它在递归调用时,x和y的顺序变了,而边界也是不难得出的:gcd(a,0)=1*a-0*0=a。这样,唯一需要记忆的就是y-=x*(a/b),看不太懂的同学自行百度好好理解一下。
上面求出了ax+by=gcd(a,b)的一组解(x1,y1),那么其他的解呢,任取另外一组解(x2,y2),则ax1+by1=ax2+by2(它们都等于gcd(a,b)),变形得
a(x1-x2)=b(y2-y1)。假设gcd(a,b)=g,方程左右两边同时除以g,得a1(x1-x2)=b1(y2-y1),其中a1=a/g,b1=b/g。注意,此时a1和b1互素,因次x1-x2一定是b的整数倍。设它为kb1,计算得y2-y1=ka1。注意,上面的推导过程并没有用到(ax+by的右边是什么),因此得出以下结论:
1.设a,b,c为任意整数。若方程ax+by=c的一组解为(x0,y0),则它的任意整数解都可以写成(x0+kb1,y0-ka1)其中a1=a/gcd(a,b),b1=b/gcd(a,b),k取任意整数。
有了这个结论,移项得ax+by=-c,然后求出一组解即可。例如:
6x+15y=9.根据扩展欧几里得算法,已经得到了6×(-2)+15×1=3,两边同时乘以3得6×(-6)+15×3=9,即x=-6,y=3时6x+15y=9。
6x+15y=8,两边除以3得2x+5y=8/3。左边是整数而右边显然不是,显然这个方程是没有整数解的。综合起来有下面的结论:
2.设a,b,c为任意整数,g=gcd(a,b),方程ax+by=g的一组解是(x0,y0),则当c是g的倍数时ax+by=c的一组解是(x0*c/g,y0*c/g);当c不是g的整数倍时无整数解。
这样这个问题就好办了,我们把上述的方程a+cx=b(modk)化为cx+ky=b-a求出一组解来即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
typedef long long ll;
using namespace std;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)//扩展欧几里得
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
exgcd(b,a%b,d,y,x);
y-=a/b*x;
}
}
int main()
{
ll a,b,c,k;
while(~scanf("%lld%lld%lld%lld",&a,&b,&c,&k))
{
if(!a&&!b&&!c&&!k)break;
k=1ll<<k;//这里注意形式,我贡献了10几发哇QAQ注意用1LL<<1
ll ans,x,y,t=b-a,shang;//t就是方程右边的数,shang用来记录b1,(在这里就是x的循环步数)
exgcd(c,k,ans,x,y);
if(t%ans==0)//如果存在整数解
{
shang=k/ans; //这里求的商是(上面所说的b1)
x=(x*(t/ans))%shang;//这里求出的x是方程的最小解
x=(x+shang)%shang;//这里因为x可能是小于0的,所以要特殊做一下处理
printf("%lld\n",x);
}
else printf("FOREVER\n");
}
return 0;
}