Description
用 m m 种字母构造一个长度为的字符串,如果一个字符串的字母重组后可以形成一个回文串则该串合法,问构成的字符串的合法子串数目期望值
Input
第一行一整数 T T 表示用例组数,每组用例输入两个整数
Output
问构成的字符串的合法子串数目期望值,结果乘 mn m n 后模 109+7 10 9 + 7
Sample Input
3
2 2
3 2
10 3
Sample Output
10
40
1908021
Solution
问题转化为求构造的所有字符串中合法子串的数目,考虑统计固定长度和起点的合法字符对答案的贡献,即长度为 i i 的合法子串在多少字符串中出现过,以表示一个长度为 i i 的字符串有种字母出现次数为奇数的方案数,那么长度为 i i 的合法串数目为,该合法子串在一个字符串中的位置有 n−i+1 n − i + 1 种情况,剩余的 n−i n − i 个字符可以随便取,故答案即为 ∑i=1ndp[i][i&1]⋅(n−i+1)⋅mn−i ∑ i = 1 n d p [ i ] [ i & 1 ] ⋅ ( n − i + 1 ) ⋅ m n − i
考虑求 dp[i][j] d p [ i ] [ j ] ,根据第 i i 个字符取之前出现奇数次的字符或者取之前出现偶数次的字符有转移
注意控制一下边界条件,以及根据 i i 和的奇偶性一致可以省去一半的复杂度
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2005;
#define mod 1000000007
int T,n,m,f[maxn],dp[maxn][maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
f[0]=1;
for(int i=1;i<=n;i++)f[i]=(ll)m*f[i-1]%mod;
dp[1][1]=m;
for(int i=2;i<=n;i++)
{
dp[i][0]=dp[i-1][1];
dp[i][i]=(ll)(m-i+1)*dp[i-1][i-1]%mod;
for(int j=2-(i&1);j<i;j+=2)
{
dp[i][j]=(ll)(m-j+1)*dp[i-1][j-1]%mod+(ll)(j+1)*dp[i-1][j+1]%mod;
if(dp[i][j]>=mod)dp[i][j]-=mod;
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
ans+=(ll)dp[i][i&1]*(n-i+1)%mod*f[n-i]%mod;
if(ans>=mod)ans-=mod;
}
printf("%d\n",ans);
}
return 0;
}