最近,对火星的一次秘密研究拉开帷幕,在这次探寻中,科学家们发现了火星上的DNA。
他们发现,火星DNA包含 (n) 个核苷酸,且由 (m) 种不同核苷酸构成(编号为 (1)~(m))。
他们还发现出于某些特殊的原因,存在 (k) 对核苷酸,一对这样的核苷酸不会连续出现在火星DNA中。
例如存在一对这样的核苷酸 (1,2) ,则核苷酸2不能紧连着核苷酸1(即DNA中不会连续出现 1,2);但是可以连续出现 2,1
科学家们想知道有多少种不同的火星DNA(如果两个DNA存在一个位置,使得两个DNA的该位置的核苷酸不同,则认为这两个DNA不同)。
直接dp好写,设(f_{i,j})表示确定了前(i)个长度的核苷酸,其中第(i)个的核苷酸的种类是(j)的方案数,于是有如下转移方程:
[f_{i,k}=sum_{j=1}^mf_{i-1,j}[mp_{j,k}=1]
]
(mp_{i,j})表示(i)后面可不可以是(j)。
然后发现这个东西可以直接矩阵加速优化,最后的答案数组就是:
[f_{1} imes mp^{n-1}
]
其中(f_{1,i}=1)。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 3000;
const int p = 1e9 + 7;
using namespace std;
struct Matrix
{
int a[61][61];
}a,f,s,c;
long long n;
int m,k,id[N + 5],ans;
char ch[3];
Matrix operator *(Matrix a,Matrix b)
{
for (int i = 0;i <= 60;i++)
for (int j = 0;j <= 60;j++)
c.a[i][j] = 0;
for (int i = 0;i <= 60;i++)
for (int j = 0;j <= 60;j++)
for (int k = 0;k <= 60;k++)
c.a[i][j] += 1ll * a.a[i][k] * b.a[k][j] % p,c.a[i][j] %= p;
return c;
}
int main()
{
scanf("%lld%d%d",&n,&m,&k);
for (int i = 1;i <= 26;i++)
{
id['a' + i - 1] = i;
id['A' + i - 1] = i + 26;
}
for (int i = 1;i <= m;i++)
for (int j = 1;j <= m;j++)
a.a[i][j] = 1;
for (int i = 1;i <= k;i++)
{
scanf("%s",ch + 1);
a.a[id[ch[1]]][id[ch[2]]] = 0;
}
for (int i = 1;i <= m;i++)
f.a[0][i] = 1;
n--;
for (int i = 0;i <= 60;i++)
s.a[i][i] = 1;
while (n)
{
if (n & 1)
s = s * a;
a = a * a;
n >>= 1;
}
f = f * s;
for (int i = 1;i <= m;i++)
ans = (ans + f.a[0][i]) % p;
cout<<ans<<endl;
return 0;
}