【特征多项式解线性递推】poj2118

叉姐论文:http://www.docin.com/p-724323397.html

a[i]=sigma(a[j]*b[k-j]) 求第n项

标准的常系数线性递推,用矩阵乘法可以做到o(k^3*logn)

但是如果用特征多项式优化的话,可以把矩阵乘法换成多项式乘法,从而做到o(k^2logn)

首先根据hamilton-cayley定理,一个矩阵的特征多项式是这个矩阵的化零多项式,因此假设最高次为第k次方项,我们可以将特征多项式的第k次方项用0~k-1次方项线性表出,同理k+1次方项可以用1~k次方项线性表出,再把第k次方项拆开,则k+1次方项也可以由0~k-1次方项线性表出,如此归纳下去,矩阵的任意次方项都可以由0~k-1次方项线性表出,那么使用快速幂的话,可以在o(k^2logn)的时间算出每一项的系数,也就是说任意次方的矩阵都可以等价于k个矩阵分别作用于向量再加起来,然后注意到这k个矩阵涉及到的最高项是a[k-1+k-1]项,也就是说可以用o(k^2)的时间算出前2k-2项的话,这k个向量也都被确定了,则要求的向量也可以计算出来。

至于如何求得特征多项式,对于这种线性递推式,按系数所在的行拉普拉斯展开,每一个余子式都可以轻易地化为对角阵的行列式(其实对角上的值都不要动就可以化为对角阵),最后可以注意到-1会被乘两次。

关于推广到一般得矩阵乘法的问题,最大的瓶颈在于最后那k个矩阵分别乘向量,容易看出,一般的递推式会涉及到前k^2项,因此光是求初项就是o(k^3)的,而一般的相似于对角阵的做法,求基变换矩阵需要高斯消元也是o(k^3)。

如果用fft做多项式乘法的话,貌似可以继续优化,但是求初项怎么办,我还不清楚。

#include 
#include 
#include 
#include 
#include 
const int mo=10000;
using namespace std;
struct Coe{
	int p[205];
	Coe operator *(const Coe &b) const;	
};
int ans[205],h[205],N,k,K[205];
int a[205],b[205];
Coe Coe::operator *(const Coe &b) const
{
	Coe c;
	for (int i=0;i<=k+k;i++) h[i]=0;
	for (int i=0;i=k;i--)
		for (int j=i-1;j>=i-k;j--)
			(h[j]+=h[i]*K[k-(i-j)])%=mo;
	for (int i=0;i1) b.p[1]=1;else b.p[0]=K[0];
	for (;e;e>>=1) {
		if (e&1) sum=sum*b;
		b=b*b;
	}
	return sum;
}
int main()
{
	for (;;) {
		scanf("%d",&k);
		if (!k) break;
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		for (int i=0;i=0;i--) scanf("%d",&b[i]);
		scanf("%d",&N);
		if (N=0;i--) {
			K[i]=b[i];
//			if ((k-1-i+1+1)&1) K[i]=-K[i];
//			if ((k-1-i)&1) K[i]=-K[i];
		}
		Coe B=fgm(N-(k-1));
		for (int i=k;i<=k+k;i++) {
			a[i]=0;
			for (int j=i-1;j>=i-k;j--) (a[i]+=a[j]*b[k-(i-j)])%=mo;
		}
		for (int i=k-1;i>=0;i--) ans[i]=0;
		for (int i=0;i<=k-1;i++)
			for (int j=i;j<=i+k-1;j++) (ans[j-i]+=a[j]*B.p[i]%mo)%=mo;
		printf("%d\n",(ans[k-1]%mo+mo)%mo);
	}
	return 0;
}

hdu4471

线性递推中有n个特殊点,因此要分成n段来做递推,直接矩乘会tle,因此每段用特征多项式递推优化。

#include 
#include 
#include 
#include 
#include 
#define f64 "%lld\n"
const int mo=1000000007;
const int lim=100;
using namespace std;
struct PP {
    long long p[600];
}b,B;
int n,m,q,t,nk[600],tk[600];
int ck[600][600],c[600],u[600];
long long F[600],f[600],h[600],tmp[600];
int vis[600];
bool cmp(int i,int j)
{
    if (nk[i]!=nk[j]) return nk[i]=lim+1;i--)
        for (int j=i-1;j>=i-lim;j--)
            (h[j]+=h[i]*c[i-j])%=mo;
    for (int i=1;i<=lim;i++) a.p[i]=h[i]; 
}
void fgm(PP &sum,long long e)
{
    for (int i=1;i<=lim;i++) sum.p[i]=B.p[i]=0;
    sum.p[1]=1;
    B.p[2]=1;
    for (;e;e>>=1) {
        if (e&1) mul(sum,B);
        mul(B,B);
    }
}
void doit(long long f[],long long e,long long F[])
{
	//cout<=1;i--,j--) F[i]=f[j];
        return ;
    }
    for (int i=2*lim+1;i<=2*lim+2*lim;i++) FF(f,i);
    for (int i=2*lim,j=2*lim+2*lim; i>=1;i--,j--) f[i]=f[j];
    //for (int i=1;i<=2*lim;i++) cout<=lim-i+1;j--,k++) {
            tmp[i]=(tmp[i]+b.p[lim-k+1]*f[j])%mo;
        }
    }
    for (int i=1;i<=lim;i++) F[i]=0;
    for (int i=1;i<=lim;i++) F[2*lim-i+1]=tmp[i];
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
    for (int test=1;scanf("%d%d%d",&n,&m,&q)==3;test++) {
        for (int i=1;i<=2*lim;i++) vis[i]=0;
        printf("Case %d: ",test);
        for (int i=1;i<=m;i++) scanf(f64,&f[i]);
        scanf("%d",&t);
        for (int i=1;i<=t;i++) scanf("%d",&c[i]);
        for (int i=t+1;i<=lim;i++) c[i]=0;
        for (int i=1;i<=q;i++) {
            scanf("%d%d",&nk[i],&tk[i]);
            if (nk[i]<=2*lim) vis[nk[i]]=i;
            for (int j=1;j<=tk[i];j++)
                scanf("%d",&ck[i][j]);
        }
        ++q;
        nk[q]=n,tk[q]=t;
        for (int i=1;i<=tk[q];i++) ck[q][i]=c[i];
        for (int i=1;i<=q;i++) u[i]=i;
        sort(u+1,u+q+1,cmp);
        int op=m;
        if (n<=m) {
        	printf(f64,f[n]);
        	continue;
        }
        for (int i=2*lim,j=m;i>=1;i--,j--) 
			if (j>0) f[i]=f[j];
			else f[i]=0;
        for (int i=1;i<=q;i++) {
        	if (nk[u[i]]<=m) continue;
            doit(f,nk[u[i]]-op-1,F);
            F[2*lim+1]=0;
            for (int j=1;j<=tk[u[i]];j++) (F[2*lim+1]+=ck[u[i]][j]*F[2*lim+1-j])%=mo;
            for (int j=1;j<=2*lim;j++) f[j]=F[j+1];
            op=nk[u[i]];
            //for (int j=2*lim;j>=1;j--) cout<


你可能感兴趣的:(hdu,poj,基本算法,数学)