Educational Codeforces Round 116 (Rated for Div. 2) 的其他题解点我
E. Arena
题目大意:
有n个人,每人有ai点生命值(ai <= k),每次每人会对其他所有人造成1点伤害。
生命值低于1的会死亡,给出 n 和 k ,问有多少种情况会出现场上无人存活
输出答案总数%998244353
2 , 1, 1 和 1 , 1 , 2 被视为两种
思路:
这题直接求好像有点困难, 我们采取间接法
也就是先求出剩下一个人的情况, 再用总数去减(最后肯定要么剩一个,要不全部死亡)
这里我们可以定义状态dp[n][k]n个人生命值在k内最后剩一个人的方案数
然后考虑状态转移
每次每个人会损失 i - 1 点生命值 ( i 为存活人数)
我们枚举每次死亡的人数 l,这些人的生命值小于i
以及所有可能的生命值j
d
p
[
i
]
[
j
]
+
=
d
p
[
i
−
l
]
[
j
−
(
i
−
1
)
]
∗
(
i
−
1
)
l
∗
C
i
l
dp[i][j] += dp[i - l][j - (i - 1)] * (i - 1)^{l} * C_i^l
dp[i][j]+=dp[i−l][j−(i−1)]∗(i−1)l∗Cil
死亡人数有 l 个,我们要从i个人中选出l个人来死亡,也就是
C
i
l
C_i^l
Cil
死亡的人中,因为收到的是 i - 1点伤害,所以他们每个人只要少于这个值都可以,总共是有l个人,也就是
(
i
−
1
)
l
(i - 1) ^ l
(i−1)l
这样解释应该能懂了吧
最后考虑最初状态
也就是只有一个人的时候
d p [ 1 ] [ j ] = j dp[1][j] = j dp[1][j]=j
AC代码:
#include <bits/stdc++.h>
#define PII pair<int,int>
#define ll long long
using namespace std;
const double eps = 1e-8;
const int maxn = 1e5 + 10;
const int mod = 998244353;
const int INF = 1<<30;
inline void swap(int &x, int &y){x^=y^=x^=y;}
inline int gcd(int a,int b) {return !b ? a : gcd(b,a%b);}
ll quick_pow(ll a, ll b){
ll ans = 1;
a %= mod;
while(b){
if(b & 1)ans *= a, ans %= mod;
a *= a;a %= mod;
b >>= 1;
}
return ans;
}
ll C[504][504];
void pre_C(int N){//递推预处理出组合数
C[0][0] = 1;
for(int i = 1; i <= N; ++i){
C[i][0] = 1;
for(int j = 1; j <= i; ++j){
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
}
ll dp[504][504];
int main(){
pre_C(501);
ll n, k;
scanf("%lld %lld", &n, &k);
for(int i = 1; i <= k; ++i)dp[1][i] = i;
for(int i = 2; i <= n; ++i){
for(int j = i; j <= k; ++j){
for(int l = 0; l < i; ++l){
dp[i][j] +=
(dp[i - l][j - i + 1] * quick_pow(i - 1, l) % mod) * C[i][l]%mod;
dp[i][j] %= mod;
}
}
}
printf("%lld\n", (quick_pow(k, n) - dp[n][k] + mod) % mod);
return 0;
}