题目大概说用k个不同的字母,有多少种方法构造出两个长度n最长公共子串长度为m的字符串。
n的规模达到了10亿,而且又是方案数,自然就想到构造矩阵用快速幂解决。
考虑用DP解决可以这么表示状态:
- dp[i][j]表示两个字符串前i个字符都构造好了 并且 它们后面的j个字符相同的方案数
状态的转移就是,末尾j个相同的可以转移到0个相同的也能转移到j+1个相同的(前提是j<m)。
而对于这个状态可以构造矩阵去转移,即一个(m+1)*(m+1)的矩阵,矩阵i行j列表示从末尾i个相同转移到末尾j个相同的方案数,而该矩阵的n次幂的第0行的和就是长度n的字符串末尾各个情况的方案数。
不过样表示状态最后求出来不是要求的,因为LCS小于m的也会包含于其中。那么减去小于m的方案数不就OK了!
- 即 至少包含m个相同公共子串的方案数 - 至少包含m-1个相同公共子串的方案数 = 恰好包含m个相同公共子串的方案数
于是,一样再构造一个m*m的矩阵求n次幂,就OK了。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <queue>
#include <map>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-4
#define maxn 100010
#define MOD 1000000007
long long n,k;
int m;
struct Matrix
{
long long a[33][33];
Matrix()
{
memset(a,0,sizeof(a));
for(int i = 0; i <= m; i++)
a[i][i] = 1;
}
};
Matrix operator*(Matrix A,Matrix B)
{
Matrix ans;
for(int i = 0; i <= m; i++)
for(int j = 0; j <= m; j++)
{
ans.a[i][j] = 0;
for(int k = 0; k <= m; k++)
ans.a[i][j] += (A.a[i][k] * B.a[k][j]) % MOD;
ans.a[i][j] %= MOD;
}
return ans;
}
Matrix Pow(Matrix A,long long n)
{
Matrix ans;
while(n)
{
if(n % 2)
ans = ans * A;
n /= 2;
A = A * A;
}
return ans;
}
int main()
{
int t,C = 1;
scanf("%d",&t);
while(t--)
{
scanf("%lld%d%lld",&n,&m,&k);
Matrix A;
memset(A.a,0,sizeof(A.a));
for(int i = 0; i <= m; i++)
A.a[0][i] = k * k - k;
for(int i = 0; i <= m-1; i++)
A.a[i+1][i] = k;
Matrix ans = Pow(A,n);
long long cnt = 0;
for(int i = 0; i <= m; i++)
{
cnt += ans.a[i][0];
cnt %= MOD;
}
memset(A.a,0,sizeof(A.a));
for(int i = 0; i <= m-1; i++)
A.a[0][i] = k * k - k;
for(int i = 0; i <= m-2; i++)
A.a[i+1][i] = k;
ans = Pow(A,n);
for(int i = 0; i <= m-1; i++)
{
cnt -= ans.a[i][0];
cnt = (cnt + MOD)%MOD;
}
printf("%lld\n",cnt);
}
return 0;
}