【题目大意】

在\(2\times N\)的方格中用\(N-1\)块\(2\times 1\)的方砖和\(2\)块\(1\times 1\)的方砖填充,且两块\(1\times 1\)的方块不能有相邻的边,求合法方案数。

【分析】

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

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

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

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

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

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

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

\[ans = 2 * \sum_{i=0}^{n-3}\sum_{j=0}^{i}f[j]*f[i-j]
\]

细细理解下。

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

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

设\(g(i)=\sum_{j=0}^{i}f(j)*f(i-j)\)。

所以

\(\begin{equation}
\begin{aligned}
g(i)&=\sum_{j=0}^{i}f(j)*f(i-j) \\
&=\sum_{j=0}^{i-2}f(j)*f(i-j)+f(i-1)*f(1)+f(i)*f(0)\\
&=\sum_{j=0}^{i-2}f(j)*[f(i-1-j)+f(i-2-j)]+f(i-1)+f(i)\\
&=g(i-2)+g(i-1)+f(i)
\end{aligned}
\end{equation}\)

又设\(sum(i)=\sum_{j=0}^{i}g(j)\)

所以

\(\begin{equation}
\begin{aligned}
sum(i)&=\sum_{j=0}^{i}g(j) \\
&=\sum_{j=0}^{i-1}g(j)+g(i) \\
&=sum(i-1)+g(i-2)+g(i-1)+f(i)
\end{aligned}
\end{equation}\)

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

\(\begin{equation}{
\left[ \begin{array}{ccc}
f(i) \\
f(i-1) \\
g(i) \\
g(i-1) \\
sum(i) \\
\end{array}
\right ]}\times {
\left[ \begin{array}{ccc}
1 & 1 & 0 & 0 & 0\\
1 & 0 & 0 & 0 & 0 \\
1 & 1 & 1 & 1 & 0 \\
0 & 0 & 1 & 0 & 0 \\
1 & 1 & 1 & 1 & 1 \\
\end{array}
\right ]}={
\left[ \begin{array}{ccc}
f(i+1)\\
f(i)\\
g(i+1)\\
g(i)\\
sum(i+1)
\end{array}
\right ]}
\end{equation}\)

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

于是\(O(125logn)\)可过。

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

【Code】

#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;
}

【详●析】[GXOI/GZOI2019]逼死强迫症的更多相关文章

  1. 【BZOJ5505】[GXOI/GZOI2019]逼死强迫症(矩阵快速幂)

    [BZOJ5505][GXOI/GZOI2019]逼死强迫症(矩阵快速幂) 题面 BZOJ 洛谷 题解 如果没有那两个\(1*1\)的东西,答案就是斐波那契数,可以简单的用\(dp\)得到. 大概是设 ...

  2. [LOJ3086][GXOI/GZOI2019]逼死强迫症——递推+矩阵乘法

    题目链接: [GXOI/GZOI2019]逼死强迫症 设$f[i][j]$表示前$i$列有$j$个$1*1$的格子的方案数,那么可以列出递推式子: $f[i][0]=f[i-1][0]+f[i-2][ ...

  3. P5303 [GXOI/GZOI2019]逼死强迫症

    题目地址:P5303 [GXOI/GZOI2019]逼死强迫症 这里是官方题解 初步分析 从题目和数据范围很容易看出来这是一个递推 + 矩阵快速幂,那么主要问题在于递推的过程. 满足条件的答案一定是以 ...

  4. luogu P5303 [GXOI/GZOI2019]逼死强迫症

    传送门 只有两行,考虑递推,设\(f_i\)为没有那两个\(1*1\)的,前\(i\)列的方案,可以发现一次可以放一个竖的或两个横的,也就是\(f_i=f_{i-1}+f_{i-2}\) 再设\(g_ ...

  5. [GXOI/GZOI2019]逼死强迫症

    题目 设我们最后的答案是\(g_n\) 我们发现在最后竖着放一个\(2\times 1\)的,和横着放两个\(1\times 2\)的就可以区分开之前的方案了 所以如果仅仅使用\(1\times 2\ ...

  6. 题解 洛谷 P5303 【[GXOI/GZOI2019]逼死强迫症】

    可以先去考虑没有\(1 \times 1\)的砖块的情况,对于最后一个位置只有两种情况,一个是竖着用一块砖铺设\(2 \times 1\),另一个为横着用两块砖铺设\(2 \times 2\). 设没 ...

  7. GXOI/GZOI2019题解

    GXOI/GZOI2019题解 P5300 [GXOI/GZOI2019]与或和 一眼题.. 显然枚举每个二进制位,答案就变成了全1子矩阵数量. 这个xjb推推,单调栈一下就行了. #include& ...

  8. 【LOJ】#3086. 「GXOI / GZOI2019」逼死强迫症

    LOJ#3086. 「GXOI / GZOI2019」逼死强迫症 这个就是设状态为\(S,j\)表示轮廓线为\(S\),然后用的1×1个数为j 列出矩阵转移 这样会算重两个边相邻的,只要算出斐波那契数 ...

  9. 「GXOI / GZOI2019」简要题解

    「GXOI / GZOI2019」简要题解 LOJ#3083. 「GXOI / GZOI2019」与或和 https://loj.ac/problem/3083 题意:求一个矩阵的所有子矩阵的与和 和 ...

随机推荐

  1. rbenv的使用

    创建: 2017/09/05 更新: 2018/02/03 增加更新rbenv和获取list没有的版本 更新: 2018/02/25 把path里面[个人主机名]全部替换为[主机名] 更新: 2018 ...

  2. hdoj5671 BestCoder Round #81 (div.2)

    对于交换行.交换列的操作,分别记录当前状态下每一行.每一列是原始数组的哪一行.哪一列即可. 对每一行.每一列加一个数的操作,也可以两个数组分别记录.注意当交换行.列的同时,也要交换增量数组. 输出时通 ...

  3. python help(int)

    class int(object) | int(x=0) -> integer | int(x, base=10) -> integer | | Convert a number or s ...

  4. bzoj 1814: Ural 1519 Formula 1【插头dp】

    设f[i][j][s]为轮廓线推到格子(i,j),状态为s的方案数 括号表示一段线的左端和右端,表示成左括号和右括号,状压的时候用1和2表示,0表示已经闭合 下面的蓝线是黄色格子的轮廓线,dp转移要把 ...

  5. 【原创】《从0开始学Elasticsearch》—document的单/批量crud

    内容目录 1.新建文档2.查询文档3.修改文档4.删除文档 1.新建文档 1). 语法1,手动指定document 的id: PUT /index_name/type_name/id{    &quo ...

  6. AForge.NET .NET2.0程序集无法在.net 4.0 中运行的解决方案

    如有雷同,不胜荣欣,若转载,请注明 最近在项目上一直使用.net4.0 framework,突然发现一个AForge.net中使用ffmepeg下的一个dll时,提示只能在2.0下运行,在众多MSDN ...

  7. android 多线程(二)

    1. 使用 AsyncTask 实现进度条 package com.test.network; import android.os.AsyncTask; import android.support. ...

  8. Web | 解决中文乱码

    设定文件的编码格式在head中添加 <head> <meta http-equiv="Content-Type" content="text/html; ...

  9. FZu Problem 2233 ~APTX4869 (并查集 + sort)

    题目链接: FZu Problem 2233 ~APTX4869 题目描述: 给一个n*n的矩阵,(i, j)表示第 i 种材料 和 第 j 种材料的影响值,这个矩阵代表这n个物品之间的影响值.当把这 ...

  10. 在HTML页面中实时获取新消息的方法 “JavaScript中的setInterval用法”

    JavaScript中的setInterval用法(资料来源:博主---八神吻你   ) setInterval动作的作用是在播放动画的时,每隔一定时间就调用函数,方法或对象.可以使用本动作更新来自数 ...