题目链接 : UVa12169 - Disgruntled Judge
解题思路
因为递推公式对10001取模,所以 a,b 的取值范围就可以缩小到 [0,10000], 最简单的就是直接对 a,b 进行 枚举,满足条件就作为正确答案。
代码
#include <iostream>
using namespace std;
const int mod = 10001;
int t, x[210];
void solve()
{
for(int a=0; a<=10000; a++)
{
for(int b=0; b<=10000; b++)
{
bool flag = true;
for(int i=2; i<=2*t; i+=2)
{
x[i] = (a*x[i-1] + b) % mod;
if(i+1<=2*t&&x[i+1]!=((a*x[i]+b)%mod))
{
flag = false;
break;
}
}
if(flag) return;
}
}
}
int main()
{
while(cin>>t)
{
for(int i=1; i<=2*t; i+=2) cin>>x[i];
solve();
for(int i=2; i<=2*t; i+=2) cout<<x[i]<<"\n";
}
return 0;
}
利用扩展欧几里得求解
上面的这种做法的效率低的原因就是,当 a 确定是,我们再次枚举了b,而我们观察递推公式,可以利用
x1
,
x2
和 a 的值直接计算 b 。
x2 =( a∗x1 + b ) % 10001x3 =( a∗x2 + b ) % 10001⇒ x3−a2∗x1 = (a+1)∗b−10001∗n
我们可以枚举 a, 然后利用扩展欧几里得直接算出 b,然后进行验证。由于是直接计算 b 所以值有可能很大,在中间的乘法运算会各种溢出,所以数组改为 long long 类型。
代码
#include <iostream>
using namespace std;
typedef long long ll;
const int mod = 10001;
ll x[210];
int t;
//扩展欧几里得
ll exp_gcd(ll a, ll b, ll &x, ll &y)
{
if(b==0)
{
x = 1;
y = 0;
return a;
}
ll g = exp_gcd(b, a%b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return g;
}
void solve()
{
for(int a=0; a<mod; a++)
{
ll n, b, g;
ll temp = x[3] - a * a * x[1];
//计算
g = exp_gcd(mod, a+1, n, b);
if(temp%g) continue;
b = temp / g * b;
//验证
bool flag = true;
for(int i=2; i<=2*t; i+=2)
{
x[i] = ((ll)x[i-1] * a + b) % mod;
if((i+1)<(2*t)&&x[i+1]!=((x[i]*a+b)%mod))
{
flag = false;
break;
}
}
if(flag) return;
}
}
int main()
{
while(cin>>t)
{
for(int i=1; i<=2*t; i+=2) cin>>x[i];
solve();
for(int i=2; i<=2*t; i+=2) cout<<x[i]<<"\n";
}
}