HDU5362 Just A String(dp + 计数)

/**
题意:长度为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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值