题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5863
题意:用k个不同的字符,形成两个长度为n的字符串中,可以有多少种构建两个字符串的方法,使得所有的连续子串中,对应的两串相同的最大子串长度为过m。
思路:建立转移矩阵,对于两串前i个字符,有两种情况:一种是前面连续相同最大子串长度没有达到过m,可以0~m-1任意一种;第二种是前面已经出现过一个相同子串长度为m的串了,现在结尾处的相同连续子串为0~m种。考虑好每种情况的转移关系,直接用矩阵快速幂计算,答案是转移n次后。把已经出现过一个相同子串长度为m的串的所有情况加和就是答案。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod 1000000007
const int maxn = 11*2;
int n,m,k;
struct node
{
LL a[maxn][maxn];
void f()
{
Clean(a,0);
rep(i,0,m-1)
a[i][0] = k * (k-1);
rep(i,1,m-1)
a[i-1][i] = k;
rep(i,m,2*m)
a[i][m] = k*(k-1);
rep(i,m+1,2*m)
a[i-1][i] = k;
a[m-1][2*m] = k;
}
void g()
{
Clean(a,0);
rep(i,0,2*m)
a[i][i] = 1;
}
};
node multi( node &x , node &y )
{
node ans;
Clean(ans.a,0);
rep(i,0,2*m)
rep(j,0,2*m)
rep(k,0,2*m)
ans.a[i][j] = ( ans.a[i][j] + x.a[i][k] * y.a[k][j] ) % mod;
return ans;
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
node x,temp;
x.g();
temp.f();
LL ans = 0;
while( n )
{
if ( n & 1 ) x = multi( temp , x );
temp = multi( temp , temp );
n >>= 1;
}
rep(i,m,2*m) ans = ( ans + x.a[0][i] ) % mod;
printf("%I64d\n",ans);
}
return 0;
}