题解 P4910 【帕秋莉的手环】

灵乌路空

2020-06-13 18:29:15

Solution

## 知识点: DP,矩阵加速 ### [原题面](https://www.luogu.com.cn/problem/P4910) ### 题目要求: >给定一个长度为 $n$ 的环,填入 金色或绿色。 >不能有两个相邻的绿色。 >多组数据, $T\le 10, n\le 10^{18}$ --- ### 分析题意 矩阵加速模板。 设 $f_{i,0/1}$ 为当前填到第 $i$ 位,第一个位置为 金色/绿色 的合法方案数。 分成第一个位置为 绿/金讨论: 1. 第一个位置为绿色时,$f_{1,0} = 0,f_{1,1} = 1$。 由于不能有两个相邻的绿色,则结尾珠子必为金色。 其对答案的贡献为 $f_{n,0}$ 2. 第一个位置为金色时,$f_{1,0} = 1, f_{1,1} = 0$。 此时结尾珠子颜色任意。 其对答案的贡献为 $f_{n,0} + f_{n,1}$ 状态转移方程: $f_{i,0} = f_{i-1,0} + f_{i-1,1}$ $f_{i,1} = f_{i-1,0}$ 复杂度 $O(Tn)$,期望得分 $\text{60pts}$。 --- 上式显然可矩阵加速,转移矩阵如下: $$\begin{bmatrix}f_{i-1,0}&f_{i-1,1}\end{bmatrix} \times \begin{bmatrix}1&1\\1&0\end{bmatrix} = \begin{bmatrix}f_{i,0}&f_{i,1}\end{bmatrix}$$ 复杂度 $O(T\log n)$,期望得分 $\text{100pts}$。 --- ### 代码实现 ```cpp // /* By:Luckyblock */ #include <cstdio> #include <ctype.h> #include <cstring> #include <algorithm> #define ll long long const ll kMod = 1e9 + 7; //============================================================= struct Matrix { ll a[2][2]; } shift, sum; ll T, n, ans; //0金 1绿 //============================================================= inline ll read() { ll f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } Matrix operator * (const Matrix &a, const Matrix &b) { Matrix c; memset(c.a, 0, sizeof(c)); for (int k = 0; k <= 1; ++ k) { for (int i = 0; i <= 1; ++ i) { for (int j = 0; j <= 1; ++ j) { c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j] % kMod) % kMod; } } } return c; } void Build(Matrix &a) { memset(a.a, 0, sizeof(a.a)); for (int i = 0; i <= 1; ++ i) a.a[i][i] = 1; } Matrix qpow(Matrix x, ll y) { Matrix ret; Build(ret); while (y) { if (y & 1) ret = ret * x; x = x * x; y >>= 1; } return ret; } //============================================================= int main() { T = read(); while (T --) { n = read(), ans = 0; memset(shift.a, 0, sizeof(shift.a)); shift.a[0][0] = shift.a[0][1] = shift.a[1][0] = 1; shift = qpow(shift, n - 1); memset(sum.a, 0, sizeof(sum.a)); sum.a[0][0] = 1, sum = sum * shift; ans = ((ans + sum.a[0][0]) % kMod + sum.a[0][1]) % kMod; memset(sum.a, 0, sizeof(sum.a)); sum.a[0][1] = 1, sum = sum * shift; ans = (ans + sum.a[0][0]) % kMod; printf("%lld\n", ans); } return 0; } ```