题意:
让你用k个字母构造出两个长度为n的a,b串,其中a,b连续对应子串的长度不能超过m,问方案数。
分析:
dp[i][j]表示现在构造了i长度,长度j后缀连续对应相等,那么dp[i][j] = dp[i-1][j-1]*k,特别地,dp[i][0] = sigma(dp[i-1][j]*(k-1)*k) (0=<j<=m).
令sum=dp[n][0]+dp[n][1]…+dp[n][m],那么sum的意义可以解释为前面出现的最长匹配<=m的方案,最后出现的最长匹配<=m的方案,而我们要求的是前面出现的最长匹配<=m,最后出现的最长匹配<=m,且两个等号至少一个成立的方案,那么去掉不合法的即可,不合法方案即为前面出现的最长匹配<=m-1的方案,最后出现的最长匹配<=m-1的方案,这就是将原来dp转移转移过程中的m变成m-1后的子问题,故做两遍矩阵快速幂,将sum值做差即为答案 。
PS:有点容斥的意思,但是没想到可以这样来计算出答案。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define mod 1000000007
int n, k;
struct Matrix
{
int r, c;
LL mat[13][13];
void init()
{
memset(mat, 0, sizeof(mat));
}
}ans;
Matrix mul(Matrix a, Matrix b)
{
Matrix ret;
ret.r=a.r;
ret.c=b.c;
for(int i=1; i<=ret.r; i++)
{
for(int j=1; j<=ret.c; j++)
{
ret.mat[i][j]=0;
for(int k=1; k<=a.c; k++)
{
ret.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
ret.mat[i][j]%=mod;
}
}
}
return ret;
}
Matrix quick_pow(Matrix a, int b)
{
int m=a.r;
Matrix ret;
ret.r=ret.c=m+1;
ret.init();
for(int i=1; i<=m+1; i++)
ret.mat[i][i]=1;
while(b)
{
if(b&1)
ret = mul(ret, a);
a = mul(a, a);
b >>= 1;
}
return ret;
}
LL solve(int m)
{
LL ans=0;
Matrix dp;
dp.init();
dp.r=m+1;dp.c=m+1;
for(int i=1; i<=m; i++)
dp.mat[i+1][i]=k;
for(int i=1; i<=m+1; i++)
dp.mat[i][m+1]=k*(k-1);
dp=quick_pow(dp, n);
for(int i = 1;i <= m+1;i++)
ans=(ans + dp.mat[m+1][i]) % mod;
return ans;
}
int t, m;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m>>k;
LL ans=(solve(m)-solve(m-1)+mod)%mod;
cout<<ans<<endl;
}
return 0;
}