UVA 12169 Disgruntled Judge (扩展欧几里得算法)
给出3个整数x1,a,b。根据递推公式xi=(a*xi-1+b) mod 10001来构造了长度为2T的数列。给出x1,x3,x5…x2T-1,求出x2,x4,x6…x2T.
根据题意,有:
x2=(ax1+b)%mod - - -
1
1
1
x3=(ax2+b)%mod - - -
2
2
2
令n=mod
由
1
1
1得x2和ax1+b同余,所以x2-(ax1+b)=ny1 - - -
3
3
3
由
2
2
2得x3和ax2+b同余,所以x3-(ax2+b)=ny2 - - -
4
4
4
. . . . .
方法一:
由
4
4
4-
3
3
3得,ny+(a+1)x2=ax1+x3
通过枚举a的值,通过扩展欧几里得算法求出x2的值,然后再根据
2
2
2式
x3=(ax2+b)%mod == ax2+b=k*mod+x3
求出b的值。
方法二:
由
4
4
4+a×
3
3
3得,ny+(a+1)b=x3-a2x1
通过枚举a的值,通过扩展欧几里得算法求出b的值
. . . . .
最后用O(T)的时间来计算序列,每计算出一个新式就进行校验,发现与原输入不符,则继续枚举a。
值得注意的是,用扩展欧几里得算法求出的x是方程ny+(a+1)x2=gcd(n,a+1)的解,需要通过再一次的变换来得到原方程的解。
在求出原方程的解后,通过解的线性变换 X=k*(n/gcd(n,a+1))+x2来得到处在[0,10000]区间内的解。
进行线性变换的方法可以通过取模进行。
X=(x%mod+mod)%mod,求得X属于[0,10000]
对于X=kmod+x 求X属于[0,mod]
X=x%mod 等价于 x=kmod+X 等价于 X=kmod+x
由于x可能为负数,固等式变成
X=(x%mod+mod)%mod
对于第一种方法后来求出的b也要在进行计算序列之前通过
b=(b%n+n)%n的操作,将其变换到[0,10000]的同余类。
根据取模的性质,有xi=(axi-1+b)%n=(axi%n+b%n)%n不然可能会溢出。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int max_n=1e4+5;
int n;
int input_x[max_n];
int output_x[max_n];
int mod=10001;
typedef long long ll;
void gcd(int a,int b,int &d,ll &x,ll &y)
{
if(!b){
d=a;x=1;y=0;
}
else {
gcd(b,a%b,d,y,x);y-=x*(a/b);
}
}
int main(void)
{
// freopen("out.txt","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&input_x[i]);
for(int a=0;a<=10000;a++)
{
ll y,x;
ll b;
int d;//d=gcd(mod,a+1)
ll c=input_x[2]-(ll)a*a*input_x[1];
gcd(mod,a+1,d,y,b);
if(c%d!=0)continue;
b*=(c/d);
// b=(b%mod+mod)%mod;
bool flag=true;
for(int i=1;i<=n;i++)
{
output_x[i]=(int)(((ll)a*input_x[i]+b)%mod);
int temp=(int)(((ll)a*output_x[i]+b)%mod);
if(i!=n&&temp!=input_x[i+1]){
flag=false;break;
}
}
if(!flag)continue;
else{
for(int i=1;i<=n;i++)
printf("%d\n",output_x[i]);
break;
}
}
// fclose(stdout);
}
//3
//17
//822
//3014