洛谷P4910 帕秋莉的手环
本题描述较为复杂,这里简单描述一下。
把一个长度为 \(n\) 的环用金色和绿色染色,求出有多少种染色方法使得相邻两个点必有一个是金色。
矩阵快速幂优化dp
先考虑如何 \(dp\),用 \(dp_{i,0}\) 表示第 \(i\) 个点为金色的方案数,\(dp_{i,1}\) 表示第 \(i\) 个点为绿色的方案数,那么
\( \begin{cases} dp_{i,0}=dp_{i-1,0}+dp_{i-1,1}& \\ dp_{i,1}=dp_{i-1,1}& \\ \end{cases} \)
因为如果 \(i\) 为金色,那么 \(i-1\) 两种颜色都可以,如果 \(i\) 为绿色,那么 \(i-1\) 只能为金色。
注意:因为这是一个环,所以要分别讨论第1个是金色或绿色的情况。
-
如果是金色,那么 \(dp_{i,0}=1\),第 \(n\) 个点两种颜色都可以,所以答案是 \(dp_{n,0}+dp_{n,1}\)
-
如果是绿色,那么 \(dp_{i,1}=1,\)第 \(n\) 个只能是金色,所以答案是 \(dp_{n,0}\)
最后加起来取模就可以了。
接下来考虑如何优化。
因为第 \(i\) 个点的答案只取决余 \(i-1\),所以可以用矩阵快速幂。
不难发现
\[\begin{bmatrix} dp_{i-1,0} & dp_{i-1,1} \\ 0 & 0 \end{bmatrix}
\quad
×
\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}
\quad
=
\begin{bmatrix} dp_{i,0} & dp_{i,1} \\ 0 & 0 \end{bmatrix}
\quad
\]
那么这道题就做完了。
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long //注意这里
using namespace std;
const int modd=1e9+7;
struct matrix
{
int num[3][3];
matrix()
{
memset(num,0,sizeof(num));
}
matrix operator * (const matrix &that) const //矩阵乘法
{
matrix r;
for(int i=1; i<=2; i++)
for(int j=1; j<=2; j++)
for(int k=1; k<=2; k++)
r.num[i][j]=(r.num[i][j]+num[i][k]*that.num[k][j]%modd)%modd;
return r;
}
matrix operator ^ (int p) //矩阵快速幂
{
matrix r,a;
memcpy(a.num,num,sizeof(num));
for(int i=1; i<=2; i++)
r.num[i][i]=1;
for(; p; p>>=1,a=a*a)
if(p&1) r=r*a;
return r;
}
};
matrix base; //转移矩阵
signed main()
{
base.num[1][1]=base.num[1][2]=base.num[2][1]=1;
int T;
scanf("%lld",&T);
while(T--)
{
int n,ans;
matrix A,B;
scanf("%lld",&n);
A.num[1][1]=1; //第1个点是金色的初始矩阵
B.num[1][2]=1; //第1个点是绿色
A=A*(base^(n-1));
B=B*(base^(n-1));
ans=(A.num[1][1]+A.num[1][2]+B.num[1][1])%modd;
printf("%lld\n",ans);
}
return 0;
}
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$