【BZOJ4162shlw loves matrix II】矩阵特征多项式+拉格朗日插值解矩阵快速幂

本来矩阵快速幂应该是k^3logn的,当n足够大的时候,就有必要有一种比较高效的算法将算法时间复杂度更多的偏移到k身上了。在初步学习了线性代数和多项式之后,相信机智的你一定可以把时间复杂度变成k^4 + n^2logn。    

4162: shlw loves matrix II

Time Limit: 30 Sec  Memory Limit: 128 MB Submit: 258  Solved: 89 [Submit][Status][Discuss]

Description

给定矩阵 M,请计算 M^n,并将其中每一个元素对 1000000007 取模输出。

Input

第 1 行包含两个整数 n,k,其中 n 使用二进制表示,可能含有前导零;
余下 k 行描述了一个 k * k 的矩阵 M。

Output

输出题目描述中要求的矩阵,格式同输入。

Sample Input

010 3 5 9 5 5 4 0 8 8 8

Sample Output

110 121 65 45 61 25 144 168 104

HINT

对于 100% 数据,满足 n <= 2^10000;k <= 50; 0 <= Mij < 10^9 +7

Source

By submittersubmitter

引入-拉格朗日插值

现在有n+1个点对(xi,yi),我们需要构造一个n次多项式F,使得F(xi) = yi。 那么有如下公式 [latex]  f(x)=\sum_{i=0}^{n-1}y_i\prod_{j=0,j\ne i}^{n-1}\frac{x-x_j}{x_i-x_j} [/latex]
相比于高斯消元n^3,显然这是n^2的。当然,在拉格朗日插值的基础上进行一些骚操作,也可以做到nlog^2n,(即多项式多点插值)。

回到正题

我们已知对于一个矩阵A,若有非0行(列)向量y,和常数x,使得Ay = xy,那么就称y为A的特征向量,x为A的特征值。我们移一个项,使得|A-Ex|y=0 (E为单位矩阵),由于y行列式不为0,那么一定|A-Ex| = 0 ,同时我们称f(x) = |A-Ex|为矩阵A的特征多项式。由于某个神奇的定理(Cayley-Hamilton定理),f(A) = 0。(啥?你说为什么A是一个矩阵而不是一个常数?我们其实把这个多项式f的x替换成矩阵变量就可以了,感性理解的话|A-A*E| = 0) 那么,如果我们带入k个变量进去,然后k^3高斯消元得到矩阵A的特征多项式之后,由于最终我们要求的是矩阵A的n次幂,即A^n,那么我们对于A^n减去若干个特征多项式f最终的值应该都不会变,那么我们就可以对A^n(把A看做多项式的x)对于特征多项式取模,这样就可以将A^n用一个k-1次多项式表示出来。这样我们就得到了一个关于矩阵的k-1次多项式,把他们加起来就得到了A^n的矩阵了。 由于其时间瓶颈在于k^4带入k+1个值进去然后k^3爆算行列式子,所以显得后面多项式的操作就可以写得十分暴躁了,就可以直接n^2乘法,n^2取模。感觉为了算一个矩阵幂挺拼的orz。 code:
#include<stdio.h>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>

using namespace std;
const int mod = 1e9+7;
int add(int x,int y) { x+=y; return x>=mod?x-mod:x; }
int mul(int x,int y) { return 1ll*x*y%mod; }
int sub(int x,int y) { x-=y; return x<0?x+mod:x; }
int k;
int ksm(int a,int b) {
    int ans = 1; a%=mod;
    for(;b;b>>=1,a=mul(a,a))
        if(b&1) ans = mul(ans,a);
    return ans;
}
struct poly{
    int o[205];
}F,kp,ANS,X;

poly operator^(const poly&a,int x){//插值(x+xi)
    poly tp = kp;
    for(int i=(k<<1);i;i--)
        tp.o[i]=add(a.o[i-1],mul(x,a.o[i]));
    tp.o[0] = mul(a.o[0],x);
    return tp;
}
poly operator*(const poly&a,int x) {
    poly tp = kp;
    for(int i=0;i<=(k<<1);i++) 
        tp.o[i] = mul(a.o[i],x);
    return tp;
}
poly operator*(const poly&aa,const poly&bb) {
    poly tp = kp;
    for(int i=0;i<=k;i++) {
        for(int j=0;j<=k;j++) {
            tp.o[i+j] = add(tp.o[i+j],mul(aa.o[i],bb.o[j]));
        }
    }
    return tp;
}
poly operator+(poly aa,poly bb) {
    poly tp = kp;
    for(int i=0;i<=(k<<1);i++) tp.o[i] = add(aa.o[i],bb.o[i]);
    return tp;
}
poly operator%(const poly &aa,const poly&bb) {
    poly tp = aa;
    for(int i=k;i>=0;i--) {
        int t = mul(tp.o[i+k],ksm(bb.o[k],mod-2));
        for(int j=0;j<=k;j++) {
            tp.o[i+j] = sub(tp.o[i+j],mul(bb.o[j],t));
        }
    }
    return tp;
}
struct mat{
    int o[55][55];
}a,b,kong,ans;
mat operator*(mat aa,mat bb) {
    mat tmp = kong;
    for(int x=1;x<=k;x++) {
        for(int y=1;y<=k;y++) {
            for(int z=1;z<=k;z++) {
                tmp.o[x][z] = add(tmp.o[x][z],mul(aa.o[x][y],bb.o[y][z]));
            }
        }
    }
    return tmp;
}
int calc(mat oo) {
    int mk = 1; int ans = 1;
    for(int i=1;i<=k;i++) {
        int id = -1;
        for(int j=i;j<=k;j++) {
            if(oo.o[j][i]) { id = j; break; }
        }
        if(id==-1) return 0;
        if(id!=i) {
            mk^=1;
            for(int j=i;j<=k;j++) swap(oo.o[i][j],oo.o[id][j]);
        }
        int iv = ksm(oo.o[i][i],mod-2);
        for(int j=i+1;j<=k;j++) {
            if(oo.o[j][i]) {
                int tt = mul(oo.o[j][i],iv);
                for(int z=1;z<=k;z++) {
                    oo.o[j][z] = sub(oo.o[j][z],mul(oo.o[i][z],tt));
                }
            }
        }
        ans = mul(ans,oo.o[i][i]);
    }
    return mk?ans:(mod-ans)%mod;
}
char ss[100005];
int main() {
    scanf("%s%d",&ss[1],&k);
    for(int i=1;i<=k;i++) {
        for(int j=1;j<=k;j++) {
            int x; scanf("%d",&x);
            a.o[i][j] = x;
        }
    }
    for(int i=0;i<=k;i++) {
        b = a; poly tmp = kp;
        tmp.o[0] = 1;
        for(int j=1;j<=k;j++) b.o[j][j] = add(b.o[j][j],mod-i);
        int y = calc(b);
        for(int j=0;j<=k;j++) {
            if(i==j) continue;
            tmp = tmp^( (mod-j)%mod );
            tmp = tmp*ksm((i-j+mod)%mod,mod-2);     
        }
        tmp = tmp*y;
        F = F + tmp;
    }
    ANS.o[0]=1; X.o[1] = 1;
    int slen = strlen(ss+1);
    for(int i=slen;i;i--) {
        if(ss[i]=='1') ANS = ANS*X%F;
        X = X*X%F;
    }

    b = kong;
    for(int i=1;i<=k;i++) b.o[i][i] = 1;
    for(int t=0;t<k;t++,b=b*a) {
        for(int i=1;i<=k;i++) {
            for(int j=1;j<=k;j++) {
                ans.o[i][j] = add(ans.o[i][j],mul(ANS.o[t],b.o[i][j]));
            }
        }
    }
    for(int i=1;i<=k;i++) {
        for(int j=1;j<=k;j++) {
            printf("%d ",ans.o[i][j]);
        }
        puts("");
    }
}
 
posted @ 2018-12-11 19:54  Newuser233  阅读(13)  评论(0)    收藏  举报