【矩阵快速幂】LOJ3086 「GXOI / GZOI2019」逼死强迫症

【题目】
LOJ
有一条 2 × n 2\times n 2×n的路,要用 n − 1 n-1 n1 1 × 2 1\times 2 1×2的相同砖块和两个 1 × 1 1\times 1 1×1的相同砖块来铺路,要求两个小砖块没有邻边,求方案数。有 T T T组数据。
n ≤ 2 × 1 0 9 , T ≤ 500 n\leq 2\times 10^9,T\leq 500 n2×109,T500

【解题思路】
首先如果没有那两个 1 × 1 1\times 1 1×1的砖块,答案就是 f i b fib fib,也就是考虑前两列的放置方式。不妨将 f i b fib fib记为 f i f_i fi

现在考虑两个 1 × 1 1\times 1 1×1,那么对于它们中间的方块,放置方式是唯一的。设答案为 g i g_i gi,实际上我们就是要考虑第二块 1 × 1 1\times 1 1×1放在哪里,它是一个 f f f的前缀和形式。不妨将 s s s记为 f f f的前缀和,那么我们有:

g i = g i − 1 + g i − 2 + 2 ⋅ s i − 3 g_i=g_{i-1}+g_{i-2}+2\cdot s_{i-3} gi=gi1+gi2+2si3
又根据 f i b fib fib的特性,我们可以得到 s i − 3 = f i − 1 − 1 s_{i-3}=f_{i-1}-1 si3=fi11,那么柿子就可以写成:
g i = g i − 1 + g i − 2 + 2 ⋅ f i − 1 − 2 g_{i}=g_{i-1}+g_{i-2}+2\cdot f_{i-1}-2 gi=gi1+gi2+2fi12
这个就可以写成矩阵的形式了,是一个 5 × 5 5\times 5 5×5的矩阵(还有常数项)。

初始 f 0 = f 1 = 1 , g 0 = g 1 = 0 f_0=f_1=1,g_0=g_1=0 f0=f1=1,g0=g1=0

复杂度 O ( T ⋅ 5 3 ⋅ log ⁡ n ) O(T\cdot 5^3\cdot \log n) O(T53logn)

【参考代码】

#include
using namespace std;

const int mod=1e9+7;

void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int mul(int x,int y){return 1ll*x*y%mod;}

struct Matrix
{
	int a[5][5];
	Matrix(){memset(a,0,sizeof(a));}
	void init(){memset(a,0,sizeof(a));}
	void one(){for(int i=0;i<5;++i)a[i][i]=1;}
	Matrix operator *(const Matrix&A)const
	{
		Matrix res;
		for(int i=0;i<5;++i) for(int j=0;j<5;++j) for(int k=0;k<5;++k)
			up(res.a[i][j],mul(a[i][k],A.a[k][j]));
		return res;
	}
}bas,beg,now;
Matrix mpow(Matrix x,int y)
{
	Matrix res;res.one();
	for(;y;y>>=1,x=x*x)if(y&1)res=res*x;
	return res;
}

int main()
{
#ifdef Durant_Lee
	freopen("LOJ3086.in","r",stdin);
	freopen("LOJ3086.out","w",stdout);
#endif
	bas.a[0][0]=bas.a[0][1]=bas.a[1][0]=bas.a[2][2]=bas.a[2][3]=bas.a[3][2]=bas.a[4][4]=1;
	bas.a[0][2]=2;bas.a[4][2]=mod-1;
	beg.a[0][0]=beg.a[0][1]=1;beg.a[0][4]=2;
	int T;scanf("%d",&T);
	while(T--)
	{
		int n;scanf("%d",&n);
		now=bas;now=beg*mpow(now,n-1);
		printf("%d\n",now.a[0][2]);
	}
	return 0;
}

你可能感兴趣的:(DP-矩阵乘法)