[JZOJ5726] 入门多项式题

题目描述

这里写图片描述
这里写图片描述

解题思路

完全不会线代的菜鸡肯定是用猎奇解法了。
当然还是得知道特征多项式是啥玩意。

特征多项式

对于一个矩阵 A ,他的特征多项式 f ( x ) = d e t ( A I x ) ,其中I为单位矩阵,x可以是矩阵或者一个数。大概长这样 f ( x ) = c 0 A 0 + c 1 A 1 . . . c n A n ,其中A的乘法为矩阵乘法。
注意到 f ( A ) = 0 ,那么这肯定是题目的可行解。
怎么解出这个多项式?随便带些x值进去插值。

知道这个,我们就知道一定存在次数在n以内的解。最优解不比特征多项式劣。
我们考虑枚举答案次数m,然后此时我们要确定系数数组,由于 A i 是可以知道的,那么就变成了 n 2 个线性方程组,可以高斯消元。时间复杂度 O ( n 5 )
我们是怎么判断可行的呢?先令c[m]=1,一种暴力判法是先用一部分方程解出c[],然后再代进剩下方程里看看对不对,但这样很麻烦。需要新思路。

假如我们已经求出最优解,那么系数数组应该是 c 0 . . . c m , c m = 1 , m n 。注意到,我们对c数组随便乘一个系数,它依然是一个可行解, c m = 1 是题目限制得来的。我们考虑把这个限制先取消掉,那么可以发现, c m 可以等于任何数,也就是说,他是一个自由元。

发现这个,我们就可以先假定m=n,然后开始高斯消元,一旦消到自由元,那么此时我们已经得到了最优的答案次数k。此时令 c k = 1 ,次数更高的系数我们令他全是0即可。再对前面的方程回代即可。

时间 O ( n 4 )

代码

#include<cstdio> 
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
//开 O2!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
typedef long long LL;
typedef double db;
const int N=70+5;
int a[N][N][N],b[N*N][N],c[N],i,j,k,l,tb,n,mo,xs;
int ksm(int x,int y)
{
    int ret=1;
    while (y)
    {
        if (y&1) ret=1ll*ret*x%mo;
        y>>=1;
        x=1ll*x*x%mo;
    }
    return ret;
}
int main()
{
    freopen("poly.in","r",stdin);
    //freopen("poly.out","w",stdout);
    scanf("%d %d",&n,&mo);
    fo(i,1,n)
        fo(j,1,n)
            scanf("%d",a[1][i]+j);
    fo(i,1,n) a[0][i][i]=1;
    fo(l,2,n)
        fo(i,1,n)
            fo(k,1,n)
                fo(j,1,n)
                    a[l][i][j]=(a[l][i][j]+1ll*a[1][i][k]*a[l-1][k][j])%mo;
    fo(i,1,n)
    fo(j,1,n)
    {
        fo(k,0,n) b[tb][k]=a[k][i][j];
        tb++;
    }
    fo(i,0,n)
    {
        fo(j,i,n*n) if (b[j][i]) break;
        if (j>n*n) break;
        fo(k,i,n) swap(b[i][k],b[j][k]);
        xs=ksm(b[i][i],mo-2);
        fo(k,i,n) b[i][k]=1ll*b[i][k]*xs%mo;
        fo(j,i+1,n*n)
        {
            xs=b[j][i];
            fo(k,i,n) b[j][k]=(b[j][k]-1ll*b[i][k]*xs)%mo;
        }
    }
    n=i;
    c[n]=1;
    fd(i,n-1,0)
    {
        c[i]=0;
        fo(j,i+1,n)
            c[i]=(c[i]-1ll*b[i][j]*c[j])%mo;
        c[i]=1ll*c[i]*ksm(b[i][i],mo-2)%mo;
        if (c[i]<0) c[i]+=mo;
    }
    printf("%d\n",n);
    fo(i,0,n) printf("%d ",c[i]);
}

猜你喜欢

转载自blog.csdn.net/zltjohn/article/details/80389281