题解【P5004 专心OI - 跳房子】
作者:互联网
本题的找规律题解到此为止。
为防止新人受到误导,不再接受新的此类题解。
以前的保留不会删除,但请不要再提交。
矩阵加速模板题吧。给一个正经的不用找规律的做法。
考虑设 \(F_n\) 表示前 \(n\) 个格子的答案,\(f_n\) 表示最后降落在 \(n\) 的方案数,显然有 \(F_n=F_{n-1}+f_n\),由于 \(f_n\) 也是由一个 \(f\) 的前缀和得来,所以得到 \(F_{n}=F_{n-1}+F_{n-m-1}\)。
这个长得就很矩阵加速了。观察数据范围发现 \(m\) 很小,而这又是一个前缀和的形式,启示我们把 \(\{F_n,F_{n-1},\cdots,F_{n-m}\}\) 都放入一个矩阵里进行转移。
考虑用原矩阵 \(\begin{bmatrix} F_{n-1}\\ F_{n-2}\\ \cdots\\ F_{n-m-1} \end{bmatrix}\) 推出目标矩阵 \(\begin{bmatrix} F_{n}\\ F_{n-1}\\ \cdots\\ F_{n-m} \end{bmatrix}\)
对于 \(i\) 满足 \(n-m\le i\le n-1\),\(F_i\) 都是原矩阵已知的元素,不用管它。
对于 \(F_n\),把它直接替换成 \(F_{n-1}+F_{n-m-1}\),这两项恰好也是我们原矩阵已知的元素。
下面构造转移矩阵。
第一行是特殊的,因为它的转移是对于 \(F_n\) 的。我们将第一个设成 \(1\),最后一个设成 \(1\),其余设成 \(0\),表示对应 \(F_{n-1}\) 和 \(F_{n-m-1}\)。
然后剩下的那些行,只需要将 \(F_i\) 对应的位置设成 \(1\) 就可以,直接继承过来。
这样就得到了这样一个矩阵(这是当 \(m=5\) 时矩阵的样子,来类比一下,应该很容易能看出来):
\[\begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 1\\ 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 0 \end{bmatrix}\]这样的话,可以构造一个 \(\left(m+1\right)\times \left(m+1\right)\) 的转移矩阵然后快速幂即可。
矩阵乘法 \(\Theta(m^3)\),快速幂 \(\Theta(\log n)\),最终复杂度 \(\Theta(m^3\log n)\)。
代码:
const int MOD(1e9+7);
inline int add(int x,int y){return x+y>=MOD?x+y-MOD:x+y;}
inline int mul(int x,int y){return 1ll*x*y%MOD;}
ll n;
int m;
struct Matrix
{
int G[30][30];
Matrix(){rep(i,1,m) rep(j,1,m) G[i][j]=0;return;}
Matrix operator*(const Matrix&x)
{
Matrix res;
rep(i,1,m) rep(j,1,m) rep(k,1,m) res.G[i][j]=add(res.G[i][j],mul(G[i][k],x.G[k][j]));
return res;
}
};
inline Matrix ksm(ll y)
{
Matrix ans,x;
rep(i,1,m) ans.G[i][i]=1;
rep(i,2,m) x.G[i][i-1]=1;
x.G[1][1]=1,x.G[1][m]=1;
while(y)
{
if(y&1) ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
int main()
{
scanf("%lld%d",&n,&m);
++m;
Matrix now=ksm(n+m-1);
printf("%d\n",now.G[1][1]);
return 0;
}
标签:bmatrix,return,Matrix,OI,int,题解,rep,矩阵,P5004 来源: https://www.cnblogs.com/UperFicial/p/16696073.html