E - Alphabet Tiles
キーワード
解説
DP で解く。
に対して、 を以下のように定義する。
番目のアルファベットを使って、長さ の文字列を作る方法の数
このとき、 文字目を 回使う場合の数は以下のようになる。
ここで、 文字目は 回使えるので、 は である。
に対して、次のように を更新する。
- のとき: , それ以外は
- のとき:
最後に、 に対して、 の総和を求める。空文字列は含まないので、 は含めないことに注意する。
制約から、 より、階乗や逆元の計算は前計算しておくとよい。
提出コード
- Go
const MOD = 998244353
func main() {
k := readInt()
C := readInts()
frac := make([]int, 1001)
frac[0] = 1
for i := 1; i < 1001; i++ {
frac[i] = frac[i-1] * i % MOD
}
inv := make([]int, 1001)
for i := 0; i < 1001; i++ {
inv[i] = modPow(frac[i], MOD-2, MOD)
}
DP := make([][]int, 27)
for i := 0; i < 27; i++ {
DP[i] = make([]int, 1001)
}
DP[0][0] = 1
for i := 1; i < 27; i++ {
for j := 0; j <= k; j++ {
for l := 0; l <= min(j, C[i-1]); l++ {
DP[i][j] += DP[i-1][j-l] * ((frac[j] * inv[l] % MOD) * inv[j-l] % MOD)
DP[i][j] %= MOD
}
}
}
ans := 0
for i := 1; i <= k; i++ {
ans += DP[26][i]
ans %= MOD
}
writeLine(ans)
}
- C++
#define MOD 998244353
#define MAX 10001
ll modpow(ll a, ll n) {
ll res = 1;
while(n > 0) {
if(n & 1) res = res * a % MOD;
a = a * a % MOD;
n >>= 1;
}
return res;
}
int main() {
ll k; cin >> k;
vector<ll> C(26); rep(i, 0, 26) cin >> C[i];
vector<ll> frac(MAX), inv(MAX);
frac[0] = 1;
rep(i, 1, MAX) frac[i] = frac[i-1] * i % MOD;
rep(i, 0, MAX) inv[i] = modpow(frac[i], MOD-2);
vector<vector<ll>> dp(27,vector<ll>(MAX,0));
dp[0][0] = 1;
rep(i, 1, 27) {
rep(j, 0, k+1) {
rep(l, 0, min(j,C[i-1])+1) {
dp[i][j] += dp[i-1][j-l] * ((frac[j] * inv[l] % MOD) * inv[j-l] % MOD);
dp[i][j] %= MOD;
}
}
}
ll ans = 0;
rep(i, 1, k+1) {
ans += dp[26][i];
ans %= MOD;
}
cout << ans << endl;
}