BZOJ 4162 shlw loves matrix II 拉格朗日插值+Cayley-Hamilton定理

题目大意:给定一个 nn 的矩阵 A ,求 Ak mod 1000000007
n50,k210000

首先先介绍一下特征值的相关内容……

对于矩阵 A ,若存在常数 λ 以及非零列向量 x ,使得 Ax=λx ,则称 λ 为矩阵 A 的一个特征值, x 为矩阵 A 的一个特征向量。

Ax=λx
Ax=λIx
(AλI)x=0
|AλI|=0
A11λA21An1A12A22λAn2A1nA2nAnnλ=0
A 的特征多项式 f(λ)=|AλI| 为关于 λ n 次多项式,则 f(λ) n 个零点即为 A n 个特征值。

如何求特征多项式?
拉格朗日插值法,分别带入 λ=x0,x1,...,xn ,求行列式得到 y0,y1,...,yn ,则 f(x)=ni=0ji(xxj)(xixj)yi ,时间复杂度 O(n4)

特征多项式有啥用?
Cayley-Hamilton定理: f(A)=0
故有 Ak=Ak mod f(A)
xk mod f(x) 是一个 n1 次多项式,快速幂求出,然后代入矩阵 A 即可。

总时间复杂度 O(n4+n2logk)

#include 
#include 
#include 
#include 
#define M 55
#define MOD 1000000007
using namespace std;

char s[10100];
int n;

int a[M][M],b[M][M],ans[M][M];
int y[M];
long long Quick_Power(long long x,int y)
{
    long long re=1;
    while(y)
    {
        if(y&1) (re*=x)%=MOD;
        (x*=x)%=MOD; y>>=1;
    }
    return re;
}
int Det(int a[M][M])
{
    int k;
    long long re=1;
    for(int i=1;i<=n;i++)
    {
        for(k=i;k<=n;k++)
            if(a[k][i])
                break;
        if(k==n+1)
            return 0;
        if(k!=i)
        {
            (re*=MOD-1)%=MOD;
            for(int j=1;j<=n;j++)
                swap(a[i][j],a[k][j]);
        }
        (re*=a[i][i])%=MOD;
        long long temp=Quick_Power(a[i][i],MOD-2);
        for(int j=1;j<=n;j++)
            a[i][j]=a[i][j]*temp%MOD;
        for(k=1;k<=n;k++)
            if(k!=i)
            {
                long long temp=(MOD-a[k][i])%MOD;
                for(int j=1;j<=n;j++)
                    (a[k][j]+=a[i][j]*temp%MOD)%=MOD;
            }
    }
    return re;
}

struct Polynomial{
    int a[M<<1];
    Polynomial(int x)
    {
        memset(a,0,sizeof a);
        a[0]=x;
    }
    int& operator [] (int x)
    {
        return a[x];
    }
    friend Polynomial operator + (Polynomial x,Polynomial y)
    {
        Polynomial z(0);
        for(int i=0;i<=n<<1;i++)
            z[i]=(x[i]+y[i])%MOD;
        return z;
    }
    friend Polynomial operator * (Polynomial x,Polynomial y)
    {
        Polynomial z(0);
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                (z[i+j]+=(long long)x[i]*y[j]%MOD)%=MOD;
        return z;
    }
    Polynomial operator * (long long x)//f(x)*a;
    {
        Polynomial re(0);
        for(int i=0;i<=n<<1;i++)
            re[i]=(a[i]*x)%MOD;
        return re;
    }
    Polynomial Times(long long x,long long y) //f(x)*(ax+b)
    {
        Polynomial re(a[0]*y%MOD);
        for(int i=1;i<=n<<1;i++)
            re[i]=(a[i-1]*x+a[i]*y)%MOD;
        return re;
    }
    friend Polynomial operator % (Polynomial a,Polynomial b) // a%b
    {
        for(int i=n;~i;i--)
        {
            long long temp=(MOD-a[i+n]*Quick_Power(b[n],MOD-2)%MOD)%MOD;
            for(int j=0;j<=n;j++)
                (a[i+j]+=b[j]*temp%MOD)%=MOD;
        }
        return a;
    }
};

int main()
{
    scanf("%s",s);
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    for(int x=0;x<=n;x++)
    {
        memcpy(b,a,sizeof a);
        for(int i=1;i<=n;i++)
            (b[i][i]+=MOD-x)%=MOD;
        y[x]=Det(b);
    }
    Polynomial p(0);
    for(int x=0;x<=n;x++)
    {
        Polynomial temp(1);
        for(int i=0;i<=n;i++)
            if(i!=x)
            {
                temp=temp.Times(1,MOD-i);
                temp=temp*Quick_Power((x-i+MOD)%MOD,MOD-2);
            }
        temp=temp*y[x];
        p=p+temp;
    }
    Polynomial x(0);x[1]=1;
    Polynomial re(1);
    for(int i=strlen(s)-1;~i;i--)
    {
        if(s[i]=='1')
            re=re*x%p;
        x=x*x%p;
    }
    memset(b,0,sizeof b);
    for(int i=1;i<=n;i++)
        b[i][i]=1;
    for(int k=0;kfor(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                (ans[i][j]+=(long long)b[i][j]*re[k]%MOD)%=MOD;
        static int c[M][M];
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    (c[i][j]+=(long long)a[i][k]*b[k][j]%MOD)%=MOD;
        memcpy(b,c,sizeof b);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            printf("%d%c",ans[i][j],j==n?'\n':' ');
    return 0;
}

你可能感兴趣的:(BZOJ,矩阵乘法,高斯消元,数学算法)