UVA12169 Disgruntled Judge 拓展欧几里得

1.题意:给出递推公式Xi = (a*Xi-1 + b)%10001,给出T,x1,x3,x5.......x2T-1 , 让你输出可能的一组x2,x4,x6.....x2T

2.思路:

(1)暴力枚举a,b

(2)拓展欧几里得

S1:因为现在已知递推公式,T,x1,x3...

则   x2 = (a*x1 + b)%10001

      x3 = (a*x2 + b)%10001

联立两个方程得:x3 = (a*(a*x1 + b)%10001 +b)%10001

可看出,如果我们已知a,b则可以由递推公式求出所有的xi,输出即可,那么怎么求出a,b,因为递推公式对10001取模,所以a,b取值范围为[0,10000],范围很小,为们可以枚举a,然后通过上述方程求出b,然后利用求出来的a,b去求所有的xi,当然如果利用这组a,b求出的x1,x3,x5与我们输入的不一样,也就是说明改组a,b只满足上述一个方程,并不能满足递推公式对应的输入。

(思路来源:计算机的重复性,计算机最擅长的就是重复工作,算法也是依据这个特性,所以暴力/枚举是最应该想到的方式!)

 S2:如何解这个取模方程组

x3 = (a*(a*x1 + b)%10001 +b)%10001

    =(a*(a*x1 + b) +b)%10001

    =(a*a*x1 + b(a+1))%10001

所以x3 + 10001*k = a*a*x1 + b(a+1)

所以(a+1)b + 10001*(-k) = x3 - a*a*x1

类比方程ax + by = c的拓展欧几里得就可以解出b,利用a,b来求递推方程并验证结果

坑点:运算会爆int(2147483647十位),用longlong(2^63-1 19位)

3.代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200 + 7;
typedef long long int LL;
LL X[maxn];
LL ex_gcd(LL a,LL b,LL &x,LL &y){
    if(b==0){ x = 1;y = 0;return a;}
    LL gcd = ex_gcd(b,a%b,x,y);
    LL temp = x;
    x = y;
    y = temp - a/b*y;
    return gcd;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int i = 1;i<=2*T;i+=2){
        scanf("%lld",&X[i]);
    }
    for(int j = 0;j<=10000;j++){
        int a = j+1;
        LL Xp,Yp;
        LL b = 10001;
        LL c = X[3] - j*j*X[1];
        LL gcd = ex_gcd(a,b,Xp,Yp);
        if(c%gcd!=0)continue;
        Xp = c/gcd*Xp;
        //cout<<j<<" "<<Xp<<endl;
        int temp = b/gcd;
        Xp = (Xp%temp + temp)%temp;
        bool flag = 1;
        for(int i = 2;i<=2*T;i++){
            LL num = (j*X[i-1] + Xp)%10001;
            if(i&1){
                if(X[i]!=num){flag = 0;break;}
            }
            else X[i] = num;
        }
        if(flag)break;
    }
    for(int i = 2;i<=2*T;i+=2){
        printf("%lld\n",X[i]);
    }
    return 0;
}

//3
//17
//822
//3014

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/86542127