「GXOI / GZOI 2019」逼死强迫症

题目传送门luogu

题目传送门loj

题目描述

ITX351 要铺一条 2 × N 2 \times N 2×N的路,为此他购买了 N块 2 × 1 2 \times 1 2×1的方砖。可是其中一块砖在运送的过程中从中间裂开了,变成了两块 1 × 1 1 \times 1 1×1的砖块!
ITX351 由此产生了一个邪恶的想法:他想要在这条路上故意把两块 1 × 1 1 \times 1 1×1的砖块分开铺,不让两块砖有相邻的边,其他砖块可以随意铺,直到整条路铺满。这样一定可以逼死自身强迫症 sea5!

也许下面的剧情你已经猜到了——他为此兴奋不已,以至于无法敲键盘。于是,他请你帮忙计算一下,有多少种方案可以让自己的阴谋得逞。

输入格式

每个测试点包含多组数据,输入文件的第一行是一个正整数 T,表示数据的组数。注意各组数据之间是独立无关的。

接下来 T 行,每行包含一个正整数 N,代表一组数据中路的长度。

输出格式

输出应包含 T行,对于每组数据,输出一个正整数,表示满足条件的方案数。

由于答案可能非常的大,你只需要输出答案对 1000000007 ( 1 0 9 10^{9} 109 + 7) 取模后的结果。
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     

简洁题意

多组数据 ( T ≤ 500 ) (T≤500) (T500),用 2 × 1 2 \times 1 2×1 的方砖铺路,其中一块断裂成 2 2 2 1 × 1 1×1 1×1 的正方形方砖,求满 2 × N ( N ≤ 2 × 1 0 9 ) 2 \times N (N≤2×10^9) 2×N(N2×109)的道路 且 2 2 2 块正方形方砖不相邻的方案数。
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     

解题报告

一、观察下列方块

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述 假如我往左边偏,另一块 1 × 1 1×1 1×1 只能在左边,故此我们得出一条重要性质:

两块 1 × 1 1×1 1×1 的正方形方砖一定是对角的,且形成一个完整的“含方块整体”,且这种整体对于每一长度只有2种。

                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     

二、考虑旁边

在这里插入图片描述

旁边的显然就是没有 1 × 1 1×1 1×1方块的普通铺路法

那么,令 f ( i ) f(i) f(i) 表示 铺 规模为 2 × i 2 \times i 2×i 的路的方案数

那么有

                                    \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \ \ \ \\ \ \\ \\ \ \\ \ \ \ }                              f ( i ) = f ( i − 1 ) + f ( i − 2 ) f(i)=f(i-1)+f(i-2) f(i)=f(i1)+f(i2)

f ( i − 1 ) f(i-1) f(i1)铺上一个竖块, f ( i − 2 ) f(i-2) f(i2) 铺上 2 2 2 个横块

其中 f ( 0 ) = 1 , f ( 1 ) = 1 , f ( 2 ) = 2 f(0)=1,f(1)=1,f(2)=2 f(0)=1,f(1)=1,f(2)=2

嗯,斐波那契

                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     

三、转移

接下来似乎很难转移

因为这种“含方块整体”的长度不定,且左右两边长度不定

考虑终极目的

                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                      A n s ( i ) Ans(i) Ans(i)表示 按要求填满 2 × N 2 \times N 2×N道路的方案数

拆成几种好计算的,能覆盖所有情况的
一般就是分为有和没有
有的人是最后一列是否有方块格
跟我最后一列是否有“含方块整体”差不多
都是为了利用到斐波那契的前缀和

那么有

g [ i ] [ 0 ] g[i][0] g[i][0]表示 在满足“含方块整体”不在最后一列时 填满 2 × N 2 \times N 2×N道路的方案数

g [ i ] [ 1 ] g[i][1] g[i][1]表示 在满足“含方块整体”在最后一列时 填满 2 × N 2 \times N 2×N道路的方案数

                                  \text{ \ \ \ \ \ \ \\ \ \ \ \ \ \ \ \ \\ \ \ \ \ \ \ \ \\ \ \ \ }                                显然有 A n s ( i ) = g [ i ] [ 0 ] + g [ i ] [ 1 ] Ans(i)=g[i][0]+g[i][1] Ans(i)=g[i][0]+g[i][1]

                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     

考虑转移:

g [ i ] [ 0 ] = A n s ( i − 1 ) + A n s ( i − 2 ) g[i][0]=Ans(i-1)+Ans(i-2) g[i][0]=Ans(i1)+Ans(i2)

g [ i ] [ 1 ] = 2 × ∑ j = 0 i − 3 f ( j ) g[i][1]=2\times \sum_{j=0}^{i-3}{f(j)} g[i][1]=2×j=0i3f(j)

枚举普通铺法的规模 0 0 0 ~ i − 3 i-3 i3 (下图黄色部分) ;

其中 × 2 \times 2 ×2的原因是 各长度"含方块整体”有2种

在这里插入图片描述
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     
                         \text{ \ \ \\ \\ \\ \ \ \ \ \ \ \ \ \\ \ \ \ }                     

h ( x ) h(x) h(x)表示斐波那契数列前 x x x 项前缀和,那么有 h ( x ) = ∑ j = 0 x − 1 f ( j ) = f ( x + 2 ) − 1 h(x)=\sum_{j=0}^{x-1}{f(j)}=f(x+2)-1 h(x)=j=0x1f(j)=f(x+2)1

注意到我这里的斐波那契虽然是从下标0开始的,但是性质是不变的 ; 后者是斐波那契的性质,读者请自行查询

g [ i ] [ 1 ] = 2 × h ( i − 3 ) = 2 × [ f ( i − 1 ) − 1 ] g[i][1]=2\times h(i-3)=2 \times[ f(i-1)-1] g[i][1]=2×h(i3)=2×[f(i1)1]

       \text{ \ \ \ \ \ }        得到 A n s ( i ) = A n s ( i − 1 ) + A n s ( i − 2 ) + 2 × f ( i − 1 ) − 2 Ans(i)=Ans(i-1)+Ans(i-2)+2 \times f(i-1)-2 Ans(i)=Ans(i1)+Ans(i2)+2×f(i1)2

题目做到这里,可以打一个50分暴力, O ( n T ) O(nT) O(nT)

               \text{ \ \ \ \ \ \ \ \ \ \ \ }               
               \text{ \ \ \ \ \ \ \ \ \ \ \ }               

四、矩阵乘法优化

⎡     1     1     2     0   − 2 ⎢                ⎡ A n s ( i − 1 ) ⎢                ⎡      A n s ( i )     ⎢ ⎡ \text{ \ \ } 1\text{ \ \ } 1\text{ \ \ } 2\text{ \ \ } 0\text{ } -2 ⎢\text{ \ \ \ \ \ \ \ \ \ \ \ } ⎡Ans(i-1)⎢ \text{ \ \ \ \ \ \ \ \ \ \ \ } ⎡ \text{ \ \ }Ans(i)\text{ \ \ } ⎢    1   1   2   0 2              Ans(i1)                  Ans(i)   
⎢     1     0     0     0     0     ⎢                ⎢ A n s ( i − 2 ) ⎢                ⎢ A n s ( i − 1 ) ⎢ ⎢\text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } ⎢ \text{ \ \ \ \ \ \ \ \ \ \ \ } ⎢ Ans(i-2)⎢ \text{ \ \ \ \ \ \ \ \ \ \ \ } ⎢Ans(i-1) ⎢    1   0   0   0   0                 Ans(i2)              Ans(i1)
⎢     0     0     1     1     0     ⎢       ×       ⎢    f ( i − 1 )     ⎢       =      ⎢        f ( i )        ⎢ ⎢\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } ⎢\text{ \ \\ \ }\times \text{ \ \ \ } ⎢\text{ \ } f(i-1)\text{ \ } ⎢ \text{ \ \\ \ }= \text{ \ \ } ⎢\text{ \ \ \ \ } f(i)\text{ \ \ \ \ \ } ⎢    0   0   1   1   0       ×       f(i1)       =          f(i)      
⎢     0     0     1     0     0     ⎢                ⎢    f ( i − 2 )     ⎢               ⎢    f ( i − 1 )     ⎢ ⎢ \text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } ⎢\text{ \ \ \ \ \ \ \ \ \ \ \ } ⎢\text{ \ } f(i-2)\text{ \ }⎢ \text{ \ \ \ \ \ \ \ \ \ \ \ } ⎢\text{ \ } f(i-1)\text{ \ \ } ⎢    0   0   1   0   0                   f(i2)                  f(i1)   
⎡     0     0     0     0     1     ⎢                ⎣            1         ⎦                ⎢            1          ⎦ ⎡ \text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } ⎢\text{ \ \ \ \ \ \ \ \ \ \ } ⎣\text{ \ \\ \ \ \ \ \ \ } 1\text{ \ \ \ \ \ \ } ⎦ \text{ \ \ \ \ \ \ \ \ \ \ \ } ⎢\text{ \ \\ \ \ \ \ } 1\text{ \ \ \ \\ \ \ } ⎦    0   0   0   0   1                          1                              1       .

目标矩阵的 m a [ i ] [ j ] ma[i][j] ma[i][j]一般认为 由左边的矩阵第 i i i行与右边矩阵第 j j j列对应相乘 再相加

矩阵快速幂 时间复杂度 O ( 5 3 T log ⁡ n ) O(5^3T\log n) O(53Tlogn)

初始矩阵(可以不一样):

⎡ A n s ( 1 ) = 0 ⎢                ⎡Ans(1)=0⎢ \text{ \ \ \ \ \ \ \ \ \ \ \ } Ans(1)=0              
⎢ A n s ( 0 ) = 0 ⎢                ⎢Ans(0)=0⎢ \text{ \ \ \ \ \ \ \ \ \ \ \ } Ans(0)=0              
⎢    f ( 1 ) = 1     ⎢       ⎢\text{ \ } f(1)=1\text{ \ } ⎢\text{ \ \\ \ }   f(1)=1       
⎢    f ( 0 ) = 1     ⎢                ⎢\text{ \ } f(0)=1\text{ \ } ⎢\text{ \ \ \ \ \ \ \ \ \ \ \ }   f(0)=1                 
⎣            1          ⎦ ⎣\text{ \ \\ \ \ \ \ \ \ } 1\text{ \ \ \ \ \ \ \ } ⎦          1                  \text{ \ \ \ \ \ \ \ }         

任一矩阵同 对角线为1,其余为0的矩阵 相乘等于自己

感性理解: m a [ i ] [ j ] ma[i][j] ma[i][j]只能由原来的那一行 乘上一个除了第 j j j个之外都为0的列

⎡     1     0     0     0     0     ⎢ ⎡ \text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0 \text{\ \ }⎢    1   0   0   0   0   
⎢     0     1     0     0     0     ⎢ ⎢\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } ⎢    0   1   0   0   0   
⎢     0     0     1     0     0     ⎢ ⎢\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } ⎢    0   0   1   0   0   
⎢     0     0     0     1     0     ⎢ ⎢ \text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } 0\text{ \ \ } ⎢    0   0   0   1   0   
⎡     0     0     0     0     1     ⎢ ⎡ \text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 0\text{ \ \ } 1\text{ \ \ } ⎢    0   0   0   0   1   .

五、上代码:

#include
#include
#define LL long long
const LL mod=1e9+7;
struct mat{
	LL a[10][10];
	mat(LL t=0){ 
		memset(a,0,sizeof(a)); 
		a[1][1]=a[2][2]=a[3][3]=a[4][4]=a[5][5]=t; 
	}
	mat operator * (const mat &B){
		mat C;
		for(int i=1;i<=5;i++)
		for(int j=1;j<=5;j++)
		for(int k=1;k<=5;k++)
			C.a[i][j]=(C.a[i][j]+a[i][k]*B.a[k][j]+mod)%mod;
		return C;
	}
}ma,st(0);
void init()
{
	st.a[1][1]=st.a[2][1]=0; st.a[3][1]=st.a[4][1]=st.a[5][1]=1;
	ma.a[1][1]=1  ,ma.a[1][2]=1  ,ma.a[1][3]=2  ,ma.a[1][4]=0  ,ma.a[1][5]=-2;
	ma.a[2][1]=1  ,ma.a[2][2]=0  ,ma.a[2][3]=0  ,ma.a[2][4]=0  ,ma.a[2][5]=0;
	ma.a[3][1]=0  ,ma.a[3][2]=0  ,ma.a[3][3]=1  ,ma.a[3][4]=1  ,ma.a[3][5]=0;
	ma.a[4][1]=0  ,ma.a[4][2]=0  ,ma.a[4][3]=1  ,ma.a[4][4]=0  ,ma.a[4][5]=0;
	ma.a[5][1]=0  ,ma.a[5][2]=0  ,ma.a[5][3]=0  ,ma.a[5][4]=0  ,ma.a[5][5]=1;
/*	1 1 2 0 -2
	1 0 0 0 0
	0 0 1 1 0 
	0 0 1 0 0
	0 0 0 0 1*/
}
void ksm(LL p)
{
	mat b=ma,ans(1);
	while(p>0){
		if(p%2==1) ans=ans*b;
		b=b*b;p>>=1;
	}/*
	for(int i=1;i<=5;i++,printf("\n"))
		for(int j=1;j<=5;j++) printf("%lld ",ans.a[i][j]);*/
	ans=ans*st;
	printf("%lld\n",ans.a[1][1]);
}
int main() 
{
	int T;LL n;init();
	scanf("%d",&T);
	while(T--){
		scanf("%lld",&n); 
		ksm(n-1);
	}
	return 0;
}

你可能感兴趣的:(矩阵乘法,动态规划Dp)