【POJ2118】Firepersons-递推+矩阵乘法
测试地址:Firepersons
题目大意:求解常系数线性递推关系:的第项(),其中和已给出。
做法:本题需要用到递推+矩阵乘法。
这题,很容易想到用矩阵乘法做到的复杂度,那么这题就做完了。
……但是有没有更好的做法呢?
令转移矩阵为,我们最后要求的是(其中为列向量,从下往上排列着)。根据一些奇妙的线性代数结论,由特征多项式以及一些奇妙定理可以得到:
对于,有。
有了这个东西,有什么用呢?我们可以得到一个重要的结论:所有都可以表示成的线性组合。这个可以用数学归纳法简单证明。
于是我们在原先矩阵快速幂的基础上,将两个的幂乘起来的过程原先是需要的,但现在我们只需要做一个多项式乘法即可,时间复杂度为。但这样得出来的是的线性组合,那么只要用上面那个结论把高于次的项往下规约即可,也是的,因此整个算法的时间复杂度为,可以做到左右的水平。
据说用FFT优化多项式乘法等可以优化到……有待学习。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=10000;
int k;
ll a[210],b[110],n;
ll tmp[210],s[110],ss[110];
void mult(ll *A,ll *B,ll *C)
{
memset(tmp,0,sizeof(tmp));
for(int i=0;i<k;i++)
for(int j=0;j<k;j++)
tmp[i+j]=(tmp[i+j]+A[i]*B[j])%mod;
for(int i=2*k-2;i>=k;i--)
{
for(int j=1;j<=k;j++)
tmp[i-j]=(tmp[i-j]+tmp[i]*b[j])%mod;
}
for(int i=0;i<k;i++)
C[i]=tmp[i];
}
void solve(ll n)
{
int nowx=1;
memset(s,0,sizeof(s));
s[0]=1;
memset(ss,0,sizeof(ss));
if (k>1) ss[1]=1;
else ss[0]=b[1];
n=n-k+1;
while(n)
{
if (n&1) mult(s,ss,s);
n>>=1;nowx<<=1;
if (nowx<k)
{
for(int i=0;i<k;i++)
ss[i]=(i==nowx);
}
else mult(ss,ss,ss);
}
for(int i=k;i<=2*k-2;i++)
{
a[i]=0;
for(int j=1;j<=k;j++)
a[i]=(a[i]+a[i-j]*b[j])%mod;
}
ll ans=0;
for(int i=0;i<k;i++)
ans=(ans+a[i+k-1]*s[i])%mod;
printf("%lld\n",ans);
}
int main()
{
while(scanf("%d",&k)&&k)
{
for(int i=0;i<k;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=k;i++)
scanf("%lld",&b[i]);
scanf("%lld",&n);
if (n<k) printf("%lld\n",a[n]);
else solve(n);
}
return 0;
}