【详●析】[GXOI/GZOI2019]逼死强迫症

【题目大意】#

2×N的方格中用N12×1的方砖和21×1的方砖填充,且两块1×1的方块不能有相邻的边,求合法方案数。

【分析】#

啊,一道计数问题。反正我开始是这样想的。

如果没有那两块很碍事的砖,这不就是斐波拉契递推吗?,,,f[i]=f[i1]+f[i2],递推走起。

好,现在来看那两块碍事的砖。

首先,我们会发现,这两块特别的砖会把整个方格分成三个部分,我们假设左右两部分刚好是完整的(即是个矩形),那么中间的块就有性质了。

仔细推一推就会发现,当这两个特殊的块间隔奇数个块时,这两个块必定在相异的两行,并且中间只有一种方案构成。

同样的,当这两个特殊的块间隔偶数个块时,这两个块必定在相同的一行,并且中间也只有一种方案构成。

又因为不能有相邻的边,于是计算公式就出来了。

ans=2i=0n3j=0if[j]f[ij]

细细理解下。

用这个大概只能得20pt,我们想想怎么优化?看到N<=2e+9的数据范围,当然要往矩阵快速幂上面想咯。

矩阵的推法各有不同吧,我们来一步一步来拆这个式子。

g(i)=j=0if(j)f(ij)

所以

g(i)=j=0if(j)f(ij)=j=0i2f(j)f(ij)+f(i1)f(1)+f(i)f(0)=j=0i2f(j)[f(i1j)+f(i2j)]+f(i1)+f(i)=g(i2)+g(i1)+f(i)

又设sum(i)=j=0ig(j)

所以

sum(i)=j=0ig(j)=j=0i1g(j)+g(i)=sum(i1)+g(i2)+g(i1)+f(i)

所以易推得矩阵转移方程:

[f(i)f(i1)g(i)g(i1)sum(i)]×[1100010000111100010011111]=[f(i+1)f(i)g(i+1)g(i)sum(i+1)]

敲公式比敲字累多了。。。

于是O(125logn)可过。

哦,这个推法有点玄学,需要处理下边界情况,具体情况见代码。

【Code】#

Copy
#include<ctime> #include<queue> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; const int MAX = 100000 + 5; const int mod = 1e9 + 7; inline int read(){ int f = 1, x = 0;char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0'||ch>'9'); do {x = x*10+ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f*x; } struct sakura { ll mar[5][5]; }A; int t, n; inline sakura mul(sakura A, sakura B) { sakura C; memset(C.mar, 0, sizeof (C.mar)); for (int i = 0;i <= 4; ++i) { for (int k = 0;k <= 4; ++k) { for (int j = 0;j <= 4; ++j) { C.mar[i][j] = (C.mar[i][j] + (A.mar[i][k] * B.mar[k][j]) % mod ) % mod; } } } return C; } inline sakura mi(sakura A, int c) { sakura B; B.mar[0][0] = 1, B.mar[1][0] = 1, B.mar[2][0] = 2, B.mar[3][0] = 1, B.mar[4][0] = 3; for (;c;c >>= 1) { if (c & 1) B = mul(A, B); A = mul(A, A); } return B; } int main(){ t = read(); while (t--) { n = read(); if (n < 3) { printf("0\n"); continue; } if (n == 3) { printf("2\n"); continue; } A.mar[0][0] = 1, A.mar[0][1] = 1, A.mar[0][2] = 0, A.mar[0][3] = 0, A.mar[0][4] = 0; A.mar[1][0] = 1, A.mar[1][1] = 0, A.mar[1][2] = 0, A.mar[1][3] = 0, A.mar[1][4] = 0; A.mar[2][0] = 1, A.mar[2][1] = 1, A.mar[2][2] = 1, A.mar[2][3] = 1, A.mar[2][4] = 0; A.mar[3][0] = 0, A.mar[3][1] = 0, A.mar[3][2] = 1, A.mar[3][3] = 0, A.mar[3][4] = 0; A.mar[4][0] = 1, A.mar[4][1] = 1, A.mar[4][2] = 1, A.mar[4][3] = 1, A.mar[4][4] = 1; sakura ans = mi(A, n - 4); ans.mar[4][0] <<= 1; ans.mar[4][0] %= mod; printf("%d\n", ans.mar[4][0]); } return 0; }
posted @   SilentEAG  阅读(259)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS