bzoj 4128: Matrix ——BSGS&&矩阵快速幂&&哈希

题目

给定矩阵A, B和模数p,求最小的正整数x满足 A^x = B(mod p).

分析

与整数的离散对数类似,只不过普通乘法换乘了矩阵乘法。

由于矩阵的求逆麻烦,使用 $A^{km-t} = B(mod \ p)$ 形式的BSGS。

然后就是判断矩阵是否相等,

一种方法是对矩阵进行Hash,

这里为了防止两个不同矩阵的Hash值冲突,使用了两个底数进行Hash。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const ull base1 = 100003 , base2 = 1000003;

struct matrix
{
    int r, c;
    int mat[75][75];
    ull h1, h2;
    matrix(){
        memset(mat, 0, sizeof(mat));
        h1 = h2 = 0;   //记得初始化
    }

    void Hash()
    {
        for(int i = 0;i < r;i++)
            for(int j = 0;j < c;j++)
                h1 = h1 * base1 + mat[i][j], h2 = h2 * base2 + mat[i][j];
    }
};
int n, p;
matrix A, B;

matrix mul(matrix A, matrix B)   //矩阵相乘
{
    matrix ret;
    ret.r = A.r; ret.c = B.c;
    for(int i = 0;i < A.r;i++)
        for(int k = 0;k < A.c;k++)
            for(int j = 0;j < B.c;j++)
            {
                ret.mat[i][j] = (ret.mat[i][j] + A.mat[i][k] * B.mat[k][j]) % p;
            }
    return ret;
}

matrix mpow(matrix A, int n)
{
    matrix ret;
    ret.r = A.r; ret.c = A.c;
    for(int i = 0;i < ret.r;i++)  ret.mat[i][i] = 1;
    while(n)
    {
        if(n & 1)  ret = mul(ret, A);
        A = mul(A, A);
        n >>= 1;
    }
    return ret;
}

map<pair<ull, ull>, int>mp;
int BSGS(matrix A, matrix B, int p)
{
    int m=sqrt(p)+1;mp.clear();
    matrix res= B;
    for(int i = 0;i < m;i++)
    {
        res.Hash();
        mp[make_pair(res.h1, res.h2)] = i;
        res = mul(A, res);
    }
    matrix mi = mpow(A, m);
    matrix tmp = mi;
    for(int i = 1;i <= m+1;i++)
    {
        tmp.Hash();
        pair<ull, ull> pa = make_pair(tmp.h1, tmp.h2);
        if(mp.count(pa))  return i*m - mp[pa];
        tmp = mul(tmp, mi);
    }
}

void debug_print(matrix a)
{
    for(int i = 0;i < a.r;i++)
    {
        for(int j = 0;j < a.c;j++){
            printf("%d ", a.mat[i][j]);
        }
        printf("\n");
    }
}

int  main()
{
    //srand(NULL);
    scanf("%d%d", &n, &p);
    A.r = A.c = n;
    for(int i = 0;i < n;i++)
    for(int j = 0;j < n;j++){
        int tmp;
        scanf("%d", &tmp);
        A.mat[i][j] = tmp;
    }
    B.r = B.c = n;
    for(int i = 0;i < n;i++)
    for(int j = 0;j < n;j++){
        int tmp;
        scanf("%d", &tmp);
        B.mat[i][j] = tmp;
    }


    ///debug_print(A);
    //debug_print(B);
    //debug_print(R);

    printf("%d\n", BSGS(A, B, p));
}
View Code

另一种方法是随机产生一个n*1的矩阵f,若A*f=B*f我们则认为这两个矩阵是相等的。为了让直接map矩阵,还要写比较函数(奇怪的是,答案还受比较函数的影响)。

注意矩阵的左乘和右乘。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
struct matrix
{
    int r, c;
    int mat[75][75];
    matrix(){
        memset(mat, 0, sizeof(mat));
    }

    bool operator < (const matrix &w) const     //???为什么会影响结果呢
    {
        for (int i=0;i< r;i++)
            if (mat[i][0]<w.mat[i][0]) return 0;
            else if (mat[i][0]>w.mat[i][0]) return 1;
        return 0;
    }
};
int n, p;
matrix A, B, R;  //R是随机矩阵

matrix mul(matrix A, matrix B)   //矩阵相乘
{
    matrix ret;
    ret.r = A.r; ret.c = B.c;
    for(int i = 0;i < A.r;i++)
        for(int k = 0;k < A.c;k++)
            for(int j = 0;j < B.c;j++)
            {
                ret.mat[i][j] = (ret.mat[i][j] + A.mat[i][k] * B.mat[k][j]) % p;
            }
    return ret;
}

matrix mpow(matrix A, int n)
{
    matrix ret;
    ret.r = A.r; ret.c = A.c;
    for(int i = 0;i < ret.r;i++)  ret.mat[i][i] = 1;
    while(n)
    {
        if(n & 1)  ret = mul(ret, A);
        A = mul(A, A);
        n >>= 1;
    }
    return ret;
}

map<matrix, int>mp;
int BSGS(matrix A, matrix B, matrix R, int p)
{
    int m=sqrt(p)+1;mp.clear();
    matrix res=mul(B, R);
    for(int i = 0;i < m;i++)
    {
        mp[res] = i;
        res = mul(A, res);
    }
    matrix mi = mpow(A, m);
    matrix tmp = mi;
    for(int i = 1;i <= m+1;i++)
    {
        matrix t = mul(tmp, R);
        if(mp.count(t))  return i*m - mp[t];
        tmp = mul(tmp, mi);
    }
}

void debug_print(matrix a)
{
    for(int i = 0;i < a.r;i++)
    {
        for(int j = 0;j < a.c;j++){
            printf("%d ", a.mat[i][j]);
        }
        printf("\n");
    }
}

int  main()
{
    //srand(NULL);
    scanf("%d%d", &n, &p);
    A.r = A.c = n;
    for(int i = 0;i < n;i++)
    for(int j = 0;j < n;j++){
        int tmp;
        scanf("%d", &tmp);
        A.mat[i][j] = tmp;
    }
    B.r = B.c = n;
    for(int i = 0;i < n;i++)
    for(int j = 0;j < n;j++){
        int tmp;
        scanf("%d", &tmp);
        B.mat[i][j] = tmp;
    }

    R.r = n, R.c = 1;
    for(int i = 0;i < n;i++)  R.mat[i][0] = rand()%(p-1) + p;

    ///debug_print(A);
    //debug_print(B);
    //debug_print(R);

    printf("%d\n", BSGS(A, B, R, p));
}
View Code

 

 

参考链接:

1. SFN1036 zoj 4128:Matrix BSGS+矩阵乘法

2. GXZlegend【bzoj4128】Matrix 矩阵乘法+Hash+BSGS

 

posted @ 2019-09-09 11:42  Rogn  阅读(344)  评论(0编辑  收藏  举报