loj #3086. 「GXOI / GZOI2019」逼死强迫症

背景:

好像也没什么。

题目传送门:

https://loj.ac/problem/3086

题意:

给一个 2 ∗ n 2*n 2n的矩阵,现在要用 2 ∗ ( n − 1 ) 2*(n-1) 2(n1)的矩形和 2 2 2 1 ∗ 1 1*1 11的正方形铺满这个矩阵,要求这两个正方形不能相邻(边不能靠在一起)。

思路 1 1 1

显然要用 d p dp dp
f i , j f_{i,j} fi,j表示在前 i i i列已经用了 j j j 1 ∗ 1 1*1 11的正方形的方案。
我们知道若没有这两块正方形,其方案数是 Fibonacci \text{Fibonacci} Fibonacci数列。
用类似的思想。
f i , 0 = f i − 1 , 0 + f i − 2 , 0 f_{i,0}=f_{i-1,0}+f_{i-2,0} fi,0=fi1,0+fi2,0
f i , 1 = f i − 1 , 1 + 2 ∗ f i − 1 , 0 f_{i,1}=f_{i-1,1}+2*f_{i-1,0} fi,1=fi1,1+2fi1,0
f i , 2 = f i − 2 , 1 + f i − 1 , 2 + f i − 2 , 2 f_{i,2}=f_{i-2,1}+f_{i-1,2}+f_{i-2,2} fi,2=fi2,1+fi1,2+fi2,2
解释一下:
[ 1 ] [1] [1]:由于没有没有这两块正方形,其方案数是 Fibonacci \text{Fibonacci} Fibonacci数列。
[ 2 ] [2] [2]:用了一块正方形,因此有两种情况:
        1. \ \ \ \ \ \ \ 1.        1.在原来的列就用了,因此方案数为 f i − 1 , 1 f_{i-1,1} fi1,1
        2. \ \ \ \ \ \ \ 2.        2.在当前仅剩的一个这一列用,那么前面肯定 i − 1 i-1 i1列没有用过,那么当前第 i i i列可以放在上面或下面,即 2 2 2种可能,因此方案数为 2 ∗ f i − 1 , 0 2*f_{i-1,0} 2fi1,0
[ 3 ] [3] [3]:用了两块正方形,因此有两种情况:
        1. \ \ \ \ \ \ \ 1.        1.在原来的列就用了,也就早就铺满了,剩下的肯定就是类似 Fibonacci \text{Fibonacci} Fibonacci数列的东西了,因此方案数为 f i − 1 , 2 + f i − 2 , 2 f_{i-1,2}+f_{i-2,2} fi1,2+fi2,2
        2. \ \ \ \ \ \ \ 2.        2.在当前第 i i i列用,那么可以发现放置方法是固定的,不会产生新的贡献,直接继承 f i − 2 , 1 f_{i-2,1} fi2,1(不会出现两个正方形在相邻两列的情况)即可。(可以自己在草稿纸上画画。)
最后矩阵乘法优化即可。

思路 2 2 2

发现上面的式子用矩阵乘法真的不好做。
因此探求一种更优秀的做法。
f i = f i − 1 + f i − 2 + 2 ∗ ∑ j = 1 i − 3 f i b j f_i=f_{i-1}+f_{i-2}+2*\sum_{j=1}^{i-3}fib_j fi=fi1+fi2+2j=1i3fibj
其中 f i b j fib_j fibj表示 Fibonacci \text{Fibonacci} Fibonacci数列第 j j j项。
为什么可以这样呢?
对于思路 1 1 1来说,由于我们只用求 f i , 2 f_{i,2} fi,2,因此可以将 [ 1 ] , [ 2 ] [1],[2] [1],[2]带入 [ 3 ] [3] [3]中,化简即可得到上面的式子。
那么 ∑ \sum 怎么消除呢?
Fibonacci \text{Fibonacci} Fibonacci数列有一个性质: ∑ j = 1 i − 3 f i b j = f i b i − 1 − 1 \sum_{j=1}^{i-3}fib_{j}=fib_{i-1}-1 j=1i3fibj=fibi11,因此有:
f i = f i − 1 + f i − 2 + 2 ∗ ( f i b i − 1 − 1 ) f_i=f_{i-1}+f_{i-2}+2*(fib_{i-1}-1) fi=fi1+fi2+2(fibi11)

f i = f i − 1 + f i − 2 + 2 f i b i − 1 − 2 f_i=f_{i-1}+f_{i-2}+2fib_{i-1}-2 fi=fi1+fi2+2fibi12
好像好了许多。
矩阵乘法如下:
[ f i f i − 1 f i b i − 1 f i b i − 2 − 2 ] ∗ [ 1 1 2 2 1 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 1 ] = [ f i + 1 f i f i b i f i b i − 1 − 2 ] \left[ \begin{matrix} f_{i}\\ f_{i-1}\\ fib_{i-1}\\ fib_{i-2}\\ -2 \end{matrix} \right]*\left[ \begin{matrix} 1&1&2&2&1\\ 1&0&0&0&0\\ 0&0&1&1&0\\ 0&0&1&0&0\\ 0&0&0&0&1 \end{matrix} \right]= \left[ \begin{matrix} f_{i+1}\\ f_{i}\\ fib_{i}\\ fib_{i-1}\\ -2 \end{matrix} \right] fifi1fibi1fibi221100010000201102010010001=fi+1fifibifibi12

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 1000000007
#define LL long long
using namespace std;
struct node
{
	LL a[5][5];
	node clear()
	{
		memset(a,0,sizeof(a));
	}
	friend node operator * (const node a,const node b)
	{
		node c;
		c.clear();
		for(int i=0;i<5;i++)
			for(int j=0;j<5;j++)
				for(int k=0;k<5;k++)
					c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
		return c;
	}
} base,ans;
	int n;
LL work(int x)
{
	base.a[0][0]=1,base.a[0][1]=1,base.a[0][2]=2,base.a[0][3]=2,base.a[0][4]=1;
	base.a[1][0]=1,base.a[1][1]=0,base.a[1][2]=0,base.a[1][3]=0,base.a[1][4]=0;
	base.a[2][0]=0,base.a[2][1]=0,base.a[2][2]=1,base.a[2][3]=1,base.a[2][4]=0;
	base.a[3][0]=0,base.a[3][1]=0,base.a[3][2]=1,base.a[3][3]=0,base.a[3][4]=0;
	base.a[4][0]=0,base.a[4][1]=0,base.a[4][2]=0,base.a[4][3]=0,base.a[4][4]=1;
	ans.clear();
	for(int i=0;i<5;i++)
		ans.a[i][i]=1;
	while(x)
	{
		if(x&1) ans=ans*base;
		base=base*base;
		x>>=1;
	}
	return ((ans.a[1][2]+ans.a[1][3])%mod-ans.a[1][4]*2%mod+mod)%mod;
}
int main()
{
	int T,x;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&x);
		printf("%lld\n",work(x-1));
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值