Codeforces-1606 E: Arena
题目
题目传送门:codeforces-1606E
题目截图
样例描述
题目大意
给定 n n n 和 x x x, 意味着有 n n n 个英雄,对第 i i i 个英雄,我们可以给他赋一个初始生命值 1 ≤ a i ≤ x 1 \le a_i \le x 1≤ai≤x。在每一轮中,所有英雄都会打起来,即每一轮,每个英雄会受到除自身外所有英雄的一点伤害。问,有多少种赋值初始生命的情况(如果某第 i i i 个英雄初始生命不同,则算不同的情况),打到最后,没有英雄存活。
题目解析
注意本题并不是使用英雄中最高初始生命进行简单地计算,因为即时有一个最高初始生命的英雄,最终仍然可能同时阵亡,e.g.
{
a
i
}
=
[
2
,
1
,
1
]
\{a_i\}=[2, 1, 1]
{ai}=[2,1,1]的情况下,他们在第一轮会同时阵亡。由于每轮,幸存者累计所受到的伤害值是相同的,因此轮数可以作为
d
p
dp
dp 中的一个变量。
设
d
p
i
,
j
dp_{i,j}
dpi,j 代表现在有
i
i
i 个英雄在受到
j
j
j 点伤害后(如上段所述,这其实就是轮数)存活,那么如果我们不使用
d
p
1
,
:
dp_{1, :}
dp1,: 去更新
d
p
0
,
:
dp_{0, :}
dp0,:,那么其实就达到了题目的要求,且结果是
d
p
0
,
:
dp_{0,:}
dp0,:的和,因为该值是通过所有
d
p
i
,
:
,
∀
i
≥
2
dp_{i, :}, \forall i \ge 2
dpi,:,∀i≥2转移过来的,i.e. 最后一轮幸存者至少有两人,且在最后一轮攻击后全部阵亡。
如果在某一轮中,我们选择
k
k
k 个英雄死亡,每次英雄阵亡时,我们计算它们能够赋初始生命的情况数,那么动态转移方程就变成了
d
p
i
,
j
=
∑
i
+
k
≠
1
d
p
i
+
k
,
j
−
(
i
+
k
−
1
)
×
C
i
+
k
k
×
(
i
+
k
−
1
)
k
dp_{i,j}=\sum_{i+k \neq 1} dp_{i+k,j-(i+k-1)} \times C_{i+k}^k \times (i+k-1)^k
dpi,j=∑i+k=1dpi+k,j−(i+k−1)×Ci+kk×(i+k−1)k,i.e. 从状态
d
p
i
+
k
,
j
−
(
i
+
k
)
dp_{i+k,j-(i+k)}
dpi+k,j−(i+k) ,进一步选择
k
k
k 个英雄阵亡,它们的初值必然在
(
j
−
(
i
+
k
−
1
)
,
j
]
(j-(i+k-1),j]
(j−(i+k−1),j] 之间。
值得注意的一点是,如果直接用该方程进行计算,那么需要遍历所有的受伤害程度,这个数值是有可能大于
x
x
x 的,比如
n
=
3
,
x
=
3
,
{
a
i
}
=
{
3
,
3
,
3
}
n=3,x=3,\{a_i\}=\{3,3,3\}
n=3,x=3,{ai}={3,3,3} 在第一轮变为
{
1
,
1
,
1
}
\{ 1, 1, 1 \}
{1,1,1}, 每个英雄遭受
2
2
2 点伤害,而第二轮它们依旧是遭受
2
2
2 点伤害之后阵亡,总共遭到了
4
>
3
=
x
4 > 3=x
4>3=x 点伤害。于是,对于这一部分,我们要单独计算一下,即算出
d
p
i
,
j
dp_{i,j}
dpi,j之后,利用
a
n
s
=
a
n
s
+
∑
i
∑
j
d
p
i
,
j
×
(
x
−
j
)
i
∀
i
j
+
i
−
1
>
x
ans=ans+\sum_i\sum_{j} dp_{i, j} \times (x - j)^i \quad \forall_i \, j+i-1>x
ans=ans+∑i∑jdpi,j×(x−j)i∀ij+i−1>x i.e. 对还剩
i
i
i 个人时,计算所有会以
>
x
>x
>x 伤害直接死亡的情况数。
当然,如果更新的时候迭代
j
j
j 为每次新增的伤害,每次超过
x
x
x 就直接变为
x
x
x,就可以直接一个三重循环写完,也没那么多事了。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 507;
const int mod = 998244353;
int C[maxn][maxn], dp[maxn][maxn];
void initC(const int& n) {
for (int i=0; i<=n; ++i) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
}
int qpow(int x, int n) {
int ans = 1;
while(n > 0) {
if(n&1) ans = (1LL * ans * x) % mod;
x = (1LL * x * x) % mod;
n >>= 1;
}
return ans;
}
int main(){
int n, x;
cin >> n >> x;
initC(n);
dp[n][0] = 1;
for(int i=n; i>=0; --i) {
for (int j = 0; j <= x; ++j) {
if (i == n && j == 0) continue;
for (int k = 0; k <= n-i; ++k) {
if ((i==0 && i + k == 1) || i+k>n) continue;
int kval = j - (i + k - 1);
if (kval < 0) continue;
if (kval >= x) continue;
dp[i][j] = (dp[i][j] + (1LL * (1LL * dp[i+k][kval] * C[i+k][k]) % mod * qpow(i+k-1, k)) % mod) % mod;
}
}
}
int ans = 0;
for(int i=0; i<=x; ++i)
ans = (ans + dp[0][i]) % mod;
for (int i = 0; i <= n; ++i) {
for(int j = 0; j < x; ++j) {
if (j + i - 1 <= x) continue;
ans = (ans + (1LL * dp[i][j] * qpow(x - j, i)) % mod) % mod;
}
}
cout << ans << endl;
return 0;
}