1 条题解
-
0
自动搬运
来自洛谷,原作者为

liangbowen
不能再摆了,,,搬运于
2025-08-24 22:05:10,当前版本为作者最后更新于2022-12-02 18:38:17,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
前言
矩阵快速幂优化 DP 经典题。
前置知识:会做简单的 DP、矩阵加速。
题目简述
给定一个长度为 的环,每个位置可以填 或 。相邻两个不能同时填 。
求方案数。多测。
朴素 DP
思路
看到题目,很容易想到环形 DP。
设 表示第 位填 的总方案数, 表示第 位填 的总方案数。
状态转移方程如下:
- 第 位填 ,则上一位只能填 。所以 。
- 第 位填 ,则上一位可以填 或 。所以 。
初始化要分类讨论一下:
- 如果第 位填 ,则第 位只能填 。所以答案算上 。
- 如果第 位填 ,则第 位可以填 或 。所以答案算上 。
把公式抄上去即可。时间复杂度 。
代码
按照题目的应该是可以拿 分的。但是只有 分。
提交记录。
#include <iostream> #include <cstdio> using namespace std; const int N = 1e6 + 5, mod = 1e9 + 7; int n, dp[N][2]; //dp[i][0/1] : 第i位填A或B void calc() { for (int i = 2; i <= n; i++) dp[i][0] = dp[i - 1][1], //第i位填1,则上一位只能填2 dp[i][1] = (dp[i - 1][0] + dp[i - 1][1]) % mod; //第i位填2,则上一位可以填1或2 } void solve() { long long ans = 0; scanf("%d", &n); dp[1][0] = 1, dp[1][1] = 0; //第一位填1, calc(), ans = (ans + dp[n][1]) % mod; //则最后一位只能填2 dp[1][0] = 0, dp[1][1] = 1; //第一位填2, calc(), ans = (ans + dp[n][0] + dp[n][1]) % mod; //则最后一位可以填1或2 cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); int T; scanf("%d", &T); while (T--) solve(); return 0; }矩阵加速
思路
如果 就 ,那顶多一道黄题。但是 是 ,哭泣。
这里用一个很常见的 trick:矩阵加速优化 DP。
我们设一组矩阵 $\begin{pmatrix} dp_{i, 0} & dp_{i, 1} \end{pmatrix}$。
然后很容易根据状态转移方程列出关系式:
$$\begin{pmatrix} dp_{i, 0} & dp_{i, 1} \end{pmatrix} \times \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} = \begin{pmatrix} dp_{i+1, 0} & dp_{i+1, 1} \end{pmatrix} $$再根据矩阵跑快速幂就秒掉了这题。
同上面的,仍然是要初始化两个(具体看代码)。
时间复杂度 ,(矩阵大小)。
你会发现这东西很小很小,所以随便过。
代码
很轻松拿到 分。跑得飞快,最慢的点 毫秒。
代码轻度整活,但是应该可以看得懂。
提交记录。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 2, mod = 1e9 + 7; namespace martix { void mul(int ANS[][N], int x[][N], int y[][N]) { int ans[N][N] = {}; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) for (int k = 0; k < N; k++) ans[i][j] = (ans[i][j] + 1ll * x[i][k] * y[k][j]) % mod; memcpy(ANS, ans, sizeof ans); } void ksm(int f[][N], int A[][N], long long y) { while (y) { if (y & 1) mul(f, f, A); mul(A, A, A); y >>= 1; } } }; using namespace martix; void solve() { long long n, ans = 0; cin >> n; int zltAK[N][N] = {1, 0}; //第一位填1 的 初始矩阵 int ioi[N][N] = { 0, 1, 1, 1 }; ksm(zltAK, ioi, n - 1), ans = (ans + zltAK[0][1]) % mod; //则最后一位只能填2 int hxyAK[N][N] = {0, 1}; //第一位填2 的 初始矩阵 int csp[N][N] = { 0, 1, 1, 1 }; ksm(hxyAK, csp, n - 1), ans = (ans + hxyAK[0][0] + hxyAK[0][1]) % mod; //则最后一位可以填1或2 cout << ans << '\n'; } int main() { int T; scanf("%d", &T); while (T--) solve(); return 0; }码字不易,希望能帮助到大家!
- 1
信息
- ID
- 3842
- 时间
- 200ms
- 内存
- 16MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者