POJ-2065 SETI 高斯消元,扩展GCD

该题题义是给定如下一个方程组:

F(1) = C1 (mod) P
F(2) = C2 (mod) P
F(3) = C3 (mod) P ...

其中F(1) = A(1,1)*x1 + A(1, 2)*x2 + A(1, 3)*x3...

其中A(i, j) = i ^ (j-1).

面对这样一个方程,我们的做法就是先不管方程右端的(mod)P,因为F(i) 和 Ci 是同余的,那么在方程两端乘以一个数是没有影响的,代入某一个方程同样是允许的。于是剩下的就是高斯消元。由于解是唯一的,因此解出来的系数矩阵一定是满秩。接下来就可一用扩展gcd求解未知元了。

代码如下:

#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;

typedef long long int Int64;

char s[100];

int length, MOD;

Int64 x[100];

int _pow(int a, int b)
{ // 快速幂
    int ret = 1;
    while (b) {
        if (b & 1) {
            ret *= a;
            ret %= MOD;
        }
        a *= a;
        a %= MOD;
        b >>= 1;
    }
    return ret;
}

struct Matrix
{
    Int64 a[100][100];
    void init()
    {
        for (int i = 1; i <= length; ++i) {
            for (int j = 1; j <= length; ++j) {
                a[i][j] = _pow(i, j-1);
            }
            a[i][length+1] = s[i];
        }
    }
    void rswap(int x, int y, int s)
    { // 用于将后面的行交换到第一行上面来
        Int64 t;
        for (int j = s; j <= length+1; ++j) {
            t = a[x][j];
            a[x][j] = a[y][j];
            a[y][j] = t;
        }
    }
    void multi(int x, Int64 M, int s)
    { // 同一行乘以某一数,当然这时为消元准备的
        for (int j = s; j <= length+1; ++j) {
            a[x][j] *= M;
        }
    }
    void relax(int x, int y, int s)
    { // 将某一方程整体迭代进另外一个表达式
        for (int j = s; j <= length + 1; ++j) {
            a[y][j] -= a[x][j];
            a[y][j] = (a[y][j] % MOD + MOD) % MOD;
            a[x][j] = (a[x][j] % MOD + MOD) % MOD;
        }
    }
    void print()
    {
        for (int i = 1; i <= length; ++i) {
            for (int j = 1; j <= length+1; ++j) {
                printf("%5I64d ", a[i][j]);
            }
            puts("");
        }
        puts("");
    }
}M;

Int64 GCD(Int64 a, Int64 b)
{
    Int64 t;
    while (b) {
        t = b;
        b = a % b;
        a = t;
    }
    return a;
}

Int64 LCM(Int64 a, Int64 b)
{
    return a / GCD(a, b) * b;
}

Int64 ext_gcd(Int64 a, Int64 b, Int64 &x, Int64 &y)
{
    Int64 temp, ret;
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    ret = ext_gcd(b, a % b, x, y);
    temp = x, x = y, y = temp - a/b*y;
    return ret;
}

void Gauss()
{
    int i = 1, k;
    Int64 a, b, sum;
    memset(x, 0, sizeof (x));
    for (int j = 1; j <= length; ++j) {
        for (k = i; k <= length; ++k) {
            if (M.a[k][j]) break; 
        }
        if (k > length) continue;
        if (k != i) {
            M.rswap(i, k, j);
        }
        for (k = i + 1; k <= length; ++k) {
            if (M.a[k][j]) {
                Int64 lcm = LCM(M.a[i][j], M.a[k][j]);
                a = lcm/M.a[i][j];
                b = lcm/M.a[k][j];
                if (a > 1) M.multi(i, a, j);
                if (b > 1) M.multi(k, b, j);
                M.relax(i, k, j);
            }
        }
        ++i;
    }
    // M.print();
    // 一定会有唯一解,所以没有进行无解判定
    for (k = i-1; k >= 1; --k) { // 当然这里可以暴力进行求解,毕竟给定的素数不是很大
        Int64 xx, yy, A, B, C = 0, L, G;
        for (int j = k+1; j <= length; ++j) {
            C += x[j] * M.a[k][j];
        }
        A = M.a[k][k], B = MOD;
        C = M.a[k][length+1] - C; 
        G = ext_gcd(A, B, xx, yy);
        L = B / G;
        xx *= C / G; 
        xx = (xx % L + L) % L;
        x[k] = xx;
    }
    for (int j = 1; j <= length; ++j) {
        printf(j == 1 ? "%I64d" : " %I64d", x[j]);
    }
    puts("");
}

int main()
{
    int N;
    scanf("%d", &N);
    while (N--) {
        scanf("%d %s", &MOD, s+1);
        length = strlen(s+1);
        for (int i = 1; i <= length; ++i) {
            if (s[i] != '*') {
                s[i] -= ('a' - 1);
            }
            else {
                s[i] = 0;
            }
        }
        M.init();
        Gauss();
    }
    return 0;
}
posted @ 2012-07-27 12:11  沐阳  阅读(406)  评论(0编辑  收藏  举报