/**
题意:长度为n的字符串,有m种字符填充,每种字符在每个位置都是等概率的,好的子串定义为:该子串中所有字符重排
之后该子串可以形成回文串,问你有多少个好的子串
思路:长度为l的好的串如果有x种,那么在长度为n的字符传中,长度为l的子串的数量共有x * (n - l + 1) * m^(n-l)个
长度为l的话左端点共有(n - l + 1)个位置可以放,剩余n-l个位置任意
设dp[i][j]:长度为i的串中,有j种字符的个数是奇数的数量:
dp[i][j] += dp[i - 1][j + 1] * (j + 1) (放入一个字符,此字符在j种字符中出现过)
dp[i][j] += dp[i - 1][j - 1] * (m - j + 1) (放入一个字符,此字符在(j - 1)种字符中没有出现过)
很卡时间, 少用取模
**/
#include<bits/stdc++.h>
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 2e3 + 10;
using namespace std;
ll n, m, T, dp[maxn][maxn];
ll p[maxn];
void solve() {
ll ans = 0; dp[0][0] = 1; p[0] = 1;
for(int i = 1; i <= n; i++) p[i] = p[i - 1] * m % mod;
for(ll i = 1; i <= n; i++) {
ll x = min(i, m); dp[i][i + 1] = dp[i][m + 1] = 0;
for(ll j = 0; j <= x; j++) {
dp[i][j] = 0;
if(j + 1 <= min(i - 1, x)) dp[i][j] += dp[i - 1][j + 1] * (j + 1);
if(j) dp[i][j] += dp[i - 1][j - 1] * (m - j + 1);
dp[i][j] %= mod;
}
ll xx = dp[i][i & 1] * p[n - i] % mod;
xx = xx * (n - i + 1) % mod;
ans = (ans + xx) % mod;
}
printf("%lld\n", ans);
}
int main() {
scanf("%lld", &T);
while(T--) {
scanf("%lld %lld", &n, &m);
//solve();
ll ans = 0; dp[0][0] = 1; p[0] = 1;
for(int i = 1; i <= n; i++) p[i] = p[i - 1] * m % mod;
for(ll i = 1; i <= n; i++) {
ll x = min(i, m); dp[i][i + 1] = dp[i][m + 1] = 0;
for(ll j = 0; j <= x; j++) {
dp[i][j] = 0;
if(j + 1 <= min(i - 1, x)) dp[i][j] += dp[i - 1][j + 1] * (j + 1); dp[i][j] %= mod;
if(j) dp[i][j] += dp[i - 1][j - 1] * (m - j + 1);
dp[i][j] %= mod;
}
ll xx = dp[i][i & 1] * p[n - i] % mod;
xx = xx * (n - i + 1) % mod;
ans = (ans + xx) % mod;
}
printf("%lld\n", ans);
}
return 0;
}
HDU5362 Just A String(dp + 计数)
最新推荐文章于 2020-07-04 15:34:58 发布