这题输入数据好坑啊。。本题解是给像我一样的蒟蒻写的,可能略显啰嗦,已经懂了的大佬可以出门右转,
去切掉IOI- 我们先分析题目,将每一个\(w_i\),它的计算方式写成如下方式:
- \(w_i=a_1*w_{i-1}+a_2*w_{i-2}+\cdots+a_n*w_{i-n}\)
- 再注意到题中的这句话:
a1, a2, ..., an是已知常数
- 所以这是一个常系数的线性递推方程。很自然的,对于这种方程,我们可以想到直接\(O(nm)\)递推(每个数都要乘n次)。
- 继续看题。这时候我们惊讶的发现了下面这个东西:
\(1\leq n\leq 100,n < m\leq 10000000\)
- 很好,跑满的时间复杂度为\(O(10^9)\),这绝对不会是这道题的正解
不然怎么会是蓝题呀! - 这怎么办呢?看起来好像没有什么优秀的办法来优化啊
怎么可能,没有怎么做题。
这时候,就该我们神奇的矩阵快速幂(或称矩阵加速)出场了!
矩阵快速幂
- 前置知识:矩阵乘法、快速幂。
- 数学姿势:
- 1.矩阵满足结合律
- 2.矩阵不满足交换律(这就要求了我们进行快速幂运算的时候必须要注意乘的顺序)
- 由以上两点我们就可以得出一个很好用的结论:当一个矩阵乘上\(k\)次另一个矩阵的时候,我们可以看做是乘上这个矩阵的\(k\)次方,而矩阵可以像数一样用二进制的方式进行快速幂运算。
- 由此我们就推出了矩阵快速幂的基本原理。
我们先来举个栗子:
- 斐波那契数列大家应该都很熟悉吧?它的递推式如下:\(F_n=F_{i-1}+F_{i-2}\)。
- 在一定的范围以内,很显然可以\(O(n)\)递推的,但是n很大呢?
- 我们可以考虑矩阵快速幂。
- 相信很多同学们都知道斐波那契数列的转移矩阵是这个形式的:
\(\left[ \begin{matrix} F_n\\ F_{n-1}\\ \end{matrix} \right]=\)\(\left[ \begin{matrix} 1 & 1\\ 1 & 0\\ \end{matrix} \right]^{n-2}*\)\(\left[ \begin{matrix} F_2\\ F_1\\ \end{matrix} \right]\) - 但是为什么是这样的可能有些人却不太了解,这里给出解释。
- 我们考虑两个矩阵相乘:
\(\left[\begin{matrix} a & b\\ c & d\\ \end{matrix} \right]=\)\(\left[\begin{matrix} a_1 & b_1\\ c_1 & d_1\\ \end{matrix} \right]*\)\(\left[\begin{matrix} a_2 & b_2\\ c_2 & d_2\\ \end{matrix} \right]\) - 由矩阵乘法的定义我们可以知道:\(a=a_1*a_2+b_1*c_2\)
- 我们将上方矩阵补齐为\(2*2\)的:
\(\left[ \begin{matrix} F_n & 0\\ F_{n-1} & 0\\ \end{matrix} \right]=\)\(\left[ \begin{matrix} 1 & 1\\ 1 & 0\\ \end{matrix} \right]^{n-2}*\)\(\left[ \begin{matrix} F_2 & 0\\ F_1 & 0\\ \end{matrix} \right]\)
- 那么当\(n=3\)时,我们有:
- \(F_3=1*F_2+1*F_1=2\)
- \(F_2=1*F_2+0*F_1=1\)
- 继续向下推,我们惊奇的发现,对每一个新得到的表示\(F_n\)的矩阵,都是由这两个式子推过来的:
- \(F_n=1*F_{n-1}+1*F_{n-2}\)
- \(F_{n-1}=1*F_{n-1}+0*F_{n-2}\)
- 我们发现它很神奇的符合了斐波那契数列的递推式,但是为什么?
- 假设我们有形式这样一个等式:
\(\left[\begin{matrix} F_n\\ F_{n-1}\\ \vdots \\ F_{n-m+1}\\ \end{matrix} \right]=\)\(\left[\begin{matrix} a_1 & a_2 & \cdots & a_m\\ b_1 & b_2 & \cdots & b_m\\ \vdots & \vdots & \vdots & \vdots\\ k_1 & k_2 & \cdots & k_m\\ \end{matrix} \right]*\)\(\left[\begin{matrix} F_{n-1}\\ F_{n-2}\\ \vdots \\ F_{n-m}\\ \end{matrix} \right]\)
- 我们进行类似的分析可以发现:对于每一个左边的目标矩阵中的\(F_i\),它都满足这样一个形式:(\(j\)代表\(F_i\)这个数所对应的矩阵中的那一行)
- \(F_i=F_{n-1}*j_1+F_{n-2}*j_3+\dots+F_{n-m}*j_m\)
- 因为斐波那契数列只和后两位有关,所以可以用一个\(2*2\)的矩阵来表示转移过程中的\(bas\)矩阵(中间那个),然后将\(F_i\)和\(F_{i-1}\)写成上面最左边的一列,而\(F_{i-1}\)和\(F_{i-2}\)写成最右边的然后依次对应着将系数填入\(bas\),就得到了这个斐波那契数列的矩阵形式的转移方程。
好了矩阵快速幂的介绍差不多就到这里,当然,听机房大佬说二维的好像也可以????这里就不管了
因为我不会啊,我们来说一下这道题。 - 我们看最开始给出的那个递推式,我们将它写成矩阵形式:
\(\left[\begin{matrix} W_i\\ W_{i-1}\\ W_{i-2}\\ \vdots \\ W_{i-n+1}\\ \end{matrix} \right]=\)\(\left[\begin{matrix} a_1 & a_2 & a_3 & \cdots & a_n\\ 1 & 0 & 0 & \cdots & 0\\ 0 & 1 & 0 & \cdots & 0\\ \vdots & \vdots & \vdots & \vdots\\ 0 & 0 & \cdots & 1 & 0\\ \end{matrix} \right]*\)\(\left[\begin{matrix} W_{i-1}\\ W_{i-2}\\ W_{i-3}\\ \vdots \\ W_{i-n}\\ \end{matrix} \right]\) - 因为我们要求第\(m\)天,所以我们需要在基础矩阵的基础上乘上\(bas\)矩阵的\(m-n\)次方(因为要推这么多次)。状态转移如下:
\(\left[\begin{matrix} W_m\\ W_{m-1}\\ W_{m-2}\\ \vdots \\ W_{m-n+1}\\ \end{matrix} \right]=\)\(\left[\begin{matrix} a_1 & a_2 & a_3 & \cdots & a_n\\ 1 & 0 & 0 & \cdots & 0\\ 0 & 1 & 0 & \cdots & 0\\ \vdots & \vdots & \vdots & \vdots\\ 0 & 0 & \cdots & 1 & 0\\ \end{matrix} \right]^{m-n}*\)\(\left[\begin{matrix} W_n\\ W_{n-1}\\ W_{n-2}\\ \vdots \\ W_1\\ \end{matrix} \right]\) - 准备工作已经做好了(DP的题基本也就一个推方程吧。。),剩下的就是记板子上代码了。
AC Code:
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define mod 4147
using namespace std;
template<class T>il read(T &x)
{
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
const int MAXN = 105;
int n,m,w[MAXN],a[MAXN];
struct Matrix{ //矩阵快速幂模板
int val[MAXN][MAXN];
Matrix(){del(val,0);}
int *operator [](int x){return val[x];}
Matrix operator *(Matrix t){
Matrix res;
for(ri i=1;i<=n;i++)
for(ri j=1;j<=n;j++)
for(ri k=1;k<=n;k++)
res[i][j]=(res[i][j]+val[i][k]*t[k][j])%mod;
return res;
}
}ans,bas;
il init(){ //准备工作:构建矩阵
for(ri i=1;i<=n;i++) ans[i][1]=w[n-i+1];
for(ri i=1;i<=n;i++) bas[1][i]=a[i];
for(ri i=2;i<=n;i++) bas[i][i-1]=1;
}
it qpow(int w){ //形式和快速幂一毛一样
while(w){
if(w&1) ans=bas*ans;//这里要注意,就像前面说的,矩阵不具有交换性,一定是bas在前!!这一点很重要
bas=bas*bas;w>>=1;
}
return ans[1][1];
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n),read(m);
for(ri i=n;i;i--) read(w[i]);
for(ri i=1;i<=n;i++) read(a[i]);
init();
printf("%d\n",qpow(m-n));
return 0;
}
原文地址:https://www.cnblogs.com/TheShadow/p/10861307.html
时间: 2024-10-28 23:46:55