題目鏈接: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;
}