P5303 [GXOI/GZOI2019]逼死强迫症
题目地址:P5303 [GXOI/GZOI2019]逼死强迫症
这里是官方题解
初步分析
从题目和数据范围很容易看出来这是一个递推 + 矩阵快速幂,那么主要问题在于递推的过程。
满足条件的答案一定是以下的形式,设 \(k = n - 1\) ,左右两边为整齐的道路,中间为长度 \(p(p \geq 3)\) 的组合块:

由 \(p\) 的奇偶性,可以得到两种不同的基本图形,即 \(1 \times 1\) 的小块在同一行( \(p\) 是偶数)和各占一行( \(p\) 是奇数)。
数学方法
左右均为普通的铺地板问题,可以用斐波那契数列表示,通过上下翻转可以得到两种不同的中间块方案,于是答案可以表示为:
\[F_k = 2 \sum_{p=3}^{n}\ \sum_{m=p}^{n}\ f_{k-m}\ f_{m-p+1}\]
其中 \(f\) 为斐波那契数列。
转化为母函数可得:
\[\sum_{k=0}^{\infty}\ x^n\ F_k = \frac{2x^3}{(1-x)(1-x-x^2)^2}\]
DP方法
思考如何构造方案,在 \(p = 3\) 时,有两个无法分割的排列 :
、
,其余方案可以通过在中心块一
侧加入一个竖向的 \(1 \times 2\) 地砖或者两个横向的 \(1 \times 2\) 地砖组成的方形获得。答案就是上述方案的所有排列数之和。
问题转化
为了更好的求解问题,将所有方案放在一棵树上,称之为“方案树”,用 \(T\) 表示。具体而言,在树上 level \(q\) 的每个节点放入 \(q\) 个配置,这些配置可以把方案拆解为 \(q\) 块可以循环排列的结构。用这种方法,每个节点的 level 数就代表该层节点的整体重复次数。那么我们的目的就是构造这样一棵树,然后求每个节点到达根节点的距离和。

构建方案树
方案树本身的递推关系可以通过如下的方式获得:
- 在 \(T_{k-1}\) 的 level \((q - 1)\) 的每个节点末端放一个竖向的 \(1 \times 2\) 地砖。按这样的规则把新的节点从左到右放到 \(T_n\) 的 level \(q\) 的节点上。
- 在 \(T_{k-2}\) 的 level \((q - 1)\) 的每个节点末端放两个横向的 \(1 \times 2\) 地砖组成的方形。按这样的规则把新的节点从左到右放入 \(T_n\) 的 level \(q\) 的其余节点上。
斐波那契树
这样抽象之后,我们得到了一棵“斐波那契树”:初始 \(T_0\) 和 \(T_1\) 是两棵单点树,对于所有的 \(k(k \geq 2)\) , \(T_k\) 的左子树是 \(T_{k-1}\) ,右子树是 \(T_{k-2}\) 。我们的问题就转化成了对于每一个 \(n\),求 \(T_k\) 上所有节点到根节点的距离之和。
将 \(T_{k-1}\) 放入 \(T_k\) 的左子树中时, \(T_{k-1}\) 的所有节点深度加深一层,那么左子树的答案就是原本的解 \(F_{k-1}\) 与 \(T_{k-1}\) 的节点个数 \(N_{k-1}\) 之和。同理,右子树的答案就是原本的解 \(F_{k-2}\) 与 \(T_{k-2}\) 的节点个数 \(N_{k-2}\) 之和。得到 DP 方程如下:
\[N_0 = N_1 = 1, N_k = N_{k-1} +N_{k-2} + 1(k \geq 2)\]
\[F_0 = F_1 = 0, F_k = F_{k-1} + F_{k-2} + N_{k-1} +N_{k-2}(k \geq 2)\]
其对应的矩阵如下:
\[
\begin{pmatrix}
F_n & F_{n - 1} & N_n & N_{n - 1} & 1
\end{pmatrix}=
\begin{pmatrix}
F_{n - 1} & F_{n - 2} & N_{n - 1} & N_{n - 2} & 1
\end{pmatrix}
\begin{pmatrix}
1 & 1 & 0 & 0 & 0\\
1 & 0 & 0 & 0 & 0\\
1 & 0 & 1 & 1 & 0\\
1 & 0 & 1 & 0 & 0\\
0 & 0 & 1 & 0 & 1
\end{pmatrix}
\]
把这个矩阵拿去就可以用矩阵快速幂直接得到答案了。
Further more
递推方程如果进一步化简可以得到:
\[F_0 = F_1 = 0, F_k = F_{k - 1} + F_{k - 2} + 2f_n - 2 (k \geq 2)\]
其中 \(f\) 为斐波那契数列。
同样可以用于本问题的求解。
std
这里提供两份
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 5;
const int MOD = 1e9 + 7;
struct Matrix {
int n, m;
long long a[MAXN][MAXN];
Matrix(int _n, int _m) : n(_n), m(_m) {
memset(a, 0, sizeof(a));
}
friend Matrix operator*(const Matrix &x, const Matrix &y) {
Matrix ans = Matrix(x.n, y.m);
for (int i = 0; i < ans.n; ++i) {
for (int j = 0; j < ans.m; ++j) {
for (int k = 0; k < x.m; ++k) {
ans.a[i][j] += x.a[i][k] * y.a[k][j] % MOD;
ans.a[i][j] %= MOD;
}
}
}
return ans;
}
};
Matrix qpow(Matrix a, int pow) {
Matrix ans = Matrix(a.n, a.m);
for (int i = 0; i < ans.n; i++) {
ans.a[i][i] = 1;
}
for (; pow; pow >>= 1) {
if (pow & 1) {
ans = ans * a;
}
a = a * a;
}
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
int T;
cin>>T;
while (T--) {
int n;
cin >> n;
if (n == 1 || n == 2) {
cout << 0 << endl;
continue;
}
n--;
Matrix f = Matrix(1, 5);
f.a[0][2] = f.a[0][3] = f.a[0][4] = 1;
Matrix fuck = Matrix(5, 5);
fuck.a[0][0] = fuck.a[1][0] = fuck.a[2][0] = fuck.a[3][0] = 1;
fuck.a[0][1] = fuck.a[2][2] = fuck.a[3][2] = fuck.a[4][2] = 1;
fuck.a[2][3] = fuck.a[4][4] = 1;
Matrix ans = f * qpow(fuck, n - 1);
cout << ans.a[0][0] << endl;
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
int a[27][27], f[27][27];
int n, mod = 1000000007;
int num(int x, int y, int z) {
return x * 9 + y * 3 + z;
}
void mul(int a[27][27], int b[27][27]) {
int c[27][27];
memset(c, 0, sizeof(c));
for (int i = 0; i < 27; i++)
for (int j = 0; j < 27; j++)
for (int k = 0; k < 27; k++)
c[i][j] = (c[i][j] + (long long)a[i][k] * b[k][j]) % mod;
memcpy(a, c, sizeof(c));
}
int main() {
for (int z = 0; z < 3; z++) {
a[num(0, 0, z)][num(0, 0, z)] = 1;
a[num(0, 0, z)][num(1, 1, z)] = 1;
if (z < 2) a[num(0, 0, z)][num(1, 2, z + 1)] = 1;
if (z < 2) a[num(0, 0, z)][num(2, 1, z + 1)] = 1;
a[num(0, 1, z)][num(1, 0, z)] = 1;
if (z < 2) a[num(0, 1, z)][num(2, 0, z + 1)] = 1;
a[num(0, 2, z)][num(0, 0, z)] = 1;
a[num(0, 2, z)][num(1, 1, z)] = 1;
if (z < 2) a[num(0, 2, z)][num(2, 1, z + 1)] = 1;
a[num(1, 0, z)][num(0, 1, z)] = 1;
if (z < 2) a[num(1, 0, z)][num(0, 2, z + 1)] = 1;
a[num(1, 1, z)][num(0, 0, z)] = 1;
a[num(1, 2, z)][num(0, 1, z)] = 1;
a[num(2, 0, z)][num(0, 0, z)] = 1;
a[num(2, 0, z)][num(1, 1, z)] = 1;
if (z < 2) a[num(2, 0, z)][num(1, 2, z + 1)] = 1;
a[num(2, 1, z)][num(1, 0, z)] = 1;
}
f[0][num(0, 0, 0)] = 1;
cin >> n;
for (; n; n >>= 1) {
if (n & 1) mul(f, a);
mul(a, a);
}
cout << ((f[0][num(0, 0, 2)] + f[0][num(0, 2, 2)]) % mod + f[0][num(2, 0, 2)]) % mod << endl;
}
P5303 [GXOI/GZOI2019]逼死强迫症的更多相关文章
- luogu P5303 [GXOI/GZOI2019]逼死强迫症
传送门 只有两行,考虑递推,设\(f_i\)为没有那两个\(1*1\)的,前\(i\)列的方案,可以发现一次可以放一个竖的或两个横的,也就是\(f_i=f_{i-1}+f_{i-2}\) 再设\(g_ ...
- 【BZOJ5505】[GXOI/GZOI2019]逼死强迫症(矩阵快速幂)
[BZOJ5505][GXOI/GZOI2019]逼死强迫症(矩阵快速幂) 题面 BZOJ 洛谷 题解 如果没有那两个\(1*1\)的东西,答案就是斐波那契数,可以简单的用\(dp\)得到. 大概是设 ...
- [LOJ3086][GXOI/GZOI2019]逼死强迫症——递推+矩阵乘法
题目链接: [GXOI/GZOI2019]逼死强迫症 设$f[i][j]$表示前$i$列有$j$个$1*1$的格子的方案数,那么可以列出递推式子: $f[i][0]=f[i-1][0]+f[i-2][ ...
- 题解 洛谷 P5303 【[GXOI/GZOI2019]逼死强迫症】
可以先去考虑没有\(1 \times 1\)的砖块的情况,对于最后一个位置只有两种情况,一个是竖着用一块砖铺设\(2 \times 1\),另一个为横着用两块砖铺设\(2 \times 2\). 设没 ...
- [GXOI/GZOI2019]逼死强迫症
题目 设我们最后的答案是\(g_n\) 我们发现在最后竖着放一个\(2\times 1\)的,和横着放两个\(1\times 2\)的就可以区分开之前的方案了 所以如果仅仅使用\(1\times 2\ ...
- 【详●析】[GXOI/GZOI2019]逼死强迫症
[详●析][GXOI/GZOI2019]逼死强迫症 脑子不够用了... [题目大意] 在\(2\times N\)的方格中用\(N-1\)块\(2\times 1\)的方砖和\(2\)块\(1\tim ...
- GXOI/GZOI2019题解
GXOI/GZOI2019题解 P5300 [GXOI/GZOI2019]与或和 一眼题.. 显然枚举每个二进制位,答案就变成了全1子矩阵数量. 这个xjb推推,单调栈一下就行了. #include& ...
- 【LOJ】#3086. 「GXOI / GZOI2019」逼死强迫症
LOJ#3086. 「GXOI / GZOI2019」逼死强迫症 这个就是设状态为\(S,j\)表示轮廓线为\(S\),然后用的1×1个数为j 列出矩阵转移 这样会算重两个边相邻的,只要算出斐波那契数 ...
- 「GXOI / GZOI2019」简要题解
「GXOI / GZOI2019」简要题解 LOJ#3083. 「GXOI / GZOI2019」与或和 https://loj.ac/problem/3083 题意:求一个矩阵的所有子矩阵的与和 和 ...
随机推荐
- 学习storm实现求和操作
1 storm求和简单操作 主要逻辑,就是spout发送数据源,blot进行处理数据,主要注意的点就是 spout这有个nextTuple自旋,和使用父类的declare..方法声明要发送到下游的名称 ...
- python中使用redis发布订阅者模型
redis发布订阅者模型: Redis提供了发布订阅功能,可以用于消息的传输,Redis的发布订阅机制包括三个部分,发布者,订阅者和Channel.发布者和订阅者都是Redis客户端,Channel则 ...
- JS 设计模式九 -- 装饰器模式
概念 装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能. 模拟传统面向对象语言的装饰者模式 //原始的飞机类 var Plane = ...
- 微信网页分享 jssdk config:invalid signature 签名错误
invalid signature签名错误.建议按如下顺序检查: 确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisi ...
- 财务CLOUD成本核算
1.关账 仓库账关账 2.应收应付是否已审核 生成财务应收应付 3.存货账关账 4.1采购存货核算 4.2零成本维护 4.3成本中心设置 4.4成本项目设置 4.5费用项目设置 4.6成本项目匹配方 ...
- yarn 在Vue框架中的常用命令
初始化项目 yarn add init 安装vue yarn add vue 安装webpack,webpack-dev-server(是一个小型的Node.js Express服务器) yarn a ...
- 如何在Linux中轻松删除源安装的软件包?
第1步:安装Stow 在这个例子中,我们使用的是CentOS,因此我们需要扩展的EPEL库.您可以使用以下命令安装它们:yum install epel-release然后,下面这段命令:yum in ...
- CodeForces 1151E Number of Components
题目链接:http://codeforces.com/problemset/problem/1151/E 题目大意: n个人排成一个序列,标号为 1~n,第 i 个人的学习成绩为 ai,现在要选出学习 ...
- Python基础:搭建开发环境(1)
1.Python语言简介 2.Python环境 Python环境产品存在多个. 2.1 CPython CPython是Python官方提供的.一般情况下提到的Python就是指CPython,CPy ...
- <TCP/IP原理> (四) IP编址
1.IP地址的基本概念:作用.结构.类型 2.特殊地址:作用.特征 网络地址.广播地址(直接.受限) 0.0.0.0 环回地址 3.单播.多播.广播地址:特征 4.专用地址:作用.范围 5.计算和应用 ...