P5303 [GXOI/GZOI2019]逼死强迫症 题解
题目描述
\(T\) 组询问,用 \(2\) 个 \(1\times 1\) 的方块和 \(n-1\) 个 \(1\times 2\) 的方块(可以旋转)铺满 \(2\times n\) 的格子,要求这 \(2\) 个 \(1\times 1\) 的方块没有公共边,求方案数,对 \(10^9+7\) 取模。
数据范围
- \(1\le T\le 500,1\le n\le 2\cdot 10^9\) 。
时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{500MB}\) 。
分析
如果只用 \(1\times 2\) 的砖块来铺 \(2\times n\) 的网格,考虑最后一列怎么填,可以得到递推式 \(f_n=f_{n-1}+f_{n-2}\) ,又因为初始值\(f_0=f_1=1\),所以答案就是斐波那契数列。
如果 \(2\) 块 \(1\times 1\) 的砖不在同一行,不妨分别在左上和右下。
那么这 \(2\) 列及其之间的列的填法唯一,又因为中间列数为奇数且至少有 \(3\) 列,所以方案数为 \(\sum_{i+j\le n-3,i+j\not\equiv n\pmod 2}f_if_j\) 。
如果 \(2\) 块 \(1\times 1\) 的砖在同一行,不妨都在第一行。
那么这 \(2\) 列及其之间的列填法也唯一,又因为中间列数为偶数且至少有 \(4\) 列,所以方案数为 \(\sum_{i+j\le n-4,i+j\equiv n\pmod 2}f_if_j\) 。
加在一起,刚好考虑了所有 \(i+j\le n-3\) 的有序对 \((i,j)\) 。
别忘了上面两个不妨,因此答案为 \(2\sum\limits_{i+j\le n-3}f_if_j\) 。
至此可以做到 \(\mathcal O(Tn)\) ,期望得分 \(50pts\) 。
斐波那契数列, \(2\cdot 10^9\) 的数据范围,很难不联想到矩阵快速幂,目标变为构建常系数线性递推关系。
令 \(h_n=\sum\limits_{i+j\le n}f_if_j\) ,则 \(h_n=h_{n-1}+\sum_{i=0}^nf_if_{n-i}\) 。
令 \(g_n=\sum_{i=0}^nf_if_{n-i}\) ,规定 \(f_{-1}=0\) ,则:
写成矩阵乘法形式:
时间复杂度 \(\mathcal O(T\cdot 5^3\log m)\) 。
#include<bits/stdc++.h>
#define vec array<int,5>
#define mat array<vec,5>
using namespace std;
const int mod=1e9+7;
int n,t;
mat operator*(mat a,mat b)
{
mat c={};
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
for(int k=0;k<5;k++)
c[i][j]=(c[i][j]+1ll*a[i][k]*b[k][j])%mod;
return c;
}
int main()
{
for(scanf("%d",&t);t--;)
{
scanf("%d",&n);
if(n<=2) printf("0\n");
else if(n==3) printf("2\n");
else
{
mat a={vec{0,1,0,1,1},vec{1,1,0,1,1},vec{0,0,0,1,1},vec{0,0,1,1,1},vec{0,0,0,0,1}};
mat res={vec{1,1,1,2,3},{},{},{},{}};///[f_0,f_1,g_0,g_1,h_1]
for(int k=n-4;k;a=a*a,k>>=1) if(k&1) res=res*a;
printf("%d\n",2*res[0][4]%mod);
}
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16103081.html
浙公网安备 33010602011771号