Educational Codeforces Round 116 (Rated for Div. 2)E:Arena
题目大意
有
n
n
n 个英雄在擂台上战斗,编号
1
−
n
1\ - n
1 −n ,依次对应血量
a
1
,
a
2
,
a
3
,
a
4
,
.
.
.
,
a
n
a_1,a_2,a_3,a_4,...,a_n
a1,a2,a3,a4,...,an ,每一个回合,没有死亡(血量不低于
1
1
1) 的英雄会对 其 他 的英雄造成
1
1
1 点血量的伤害。
编写程序完成任务:有多少种合理的
a
1
,
a
2
,
a
3
,
a
4
,
.
.
.
,
a
n
a_1,a_2,a_3,a_4,...,a_n
a1,a2,a3,a4,...,an ,即初始血量方案,满足:
1)最终所有英雄都死亡(血量低于
1
1
1)。
2) 所有英雄的血量不超过
k
k
k 。
具体题意见原题。
题解
由题目中的数据范围
n
,
k
≤
500
n,k \leq 500
n,k≤500 可以很容易想到一个二维的递推,对于
f
(
i
,
j
)
f(i,j)
f(i,j) 这个状态,我们定义它为:有
i
i
i 个英雄,最高血量不超过
j
j
j 时的答案。
那么,怎么将这个状态从前面转移过来呢?
我们假设,一回合过后,还剩余
k
k
k 个英雄,并且这一回合造成的伤害为
i
−
1
i-1
i−1 ,那么,这时的方案数就变成了
f
(
k
,
j
−
i
+
1
)
f(k,j-i+1)
f(k,j−i+1)直接转移过来就好了。由于这剩余的
k
k
k 个英雄可以是我们枚举到的
i
i
i个英雄的任意
k
k
k 个。 所以转移方程如下:
f
(
i
,
j
)
=
f
(
i
,
j
)
+
f
(
k
,
j
−
i
+
1
)
∗
C
i
k
f(i,j)=f(i,j)+f(k,j-i+1)*C_i^k
f(i,j)=f(i,j)+f(k,j−i+1)∗Cik
由于我们只关注
f
(
n
,
k
)
f(n,k)
f(n,k) 的答案,所以有些状态是没有必要的,换言之,并不是所有的
f
(
i
,
j
)
f(i,j)
f(i,j) 都需要求解。
观察上面这个转移方程,不难发现,
j
≤
m
j\leq m
j≤m,所以
j
−
i
+
1
≤
m
−
i
+
1
j-i+1 \leq m-i+1
j−i+1≤m−i+1 ,即在这之前的
f
(
i
′
,
j
′
)
(
j
′
>
m
−
i
+
1
)
f(i',j')(j'>m-i+1)
f(i′,j′)(j′>m−i+1),都是没有必要的,不处理它们可以大大提高我们程序的效率!!
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int mod=998244353;
int sum[501];
int Qpow(int x,int k)
{
int res=1;
while(k)
{
if(k%2==1)
{
res*=x;
res%=mod;
}
x*=x;x%=mod;k>>=1;
}
return res;
}
int f[501][501];
int c[502][502];
void inz()
{
for(int i=0;i<=500;++i)
{
for(int j=0;j<=i;++j)
{
if(j==0)c[i][j]=1;
else c[i][j]=c[i-1][j-1]+c[i-1][j];
c[i][j]%=mod;
}
}
}
int limit[500];
signed main()
{
ios::sync_with_stdio(0);
cin>>n>>m;
inz();
for(int i=0;i<=m;++i){f[2][i]=i;f[0][i]=1;}
for(register int i=3;i<=n;++i)
{
int pL=i-1;
for(register int j=1;j<=(n==i?m:m-i+2);++j)
{
int Ans=0;
if(pL>=j){f[i][j]=Qpow(j,i);continue;}
for(register int k=0;k<=i;++k)
{
int tmp=Qpow(pL,i-k);
Ans=(Ans+(c[i][k])*(f[k][j-pL]*tmp%mod))%mod;
}
Ans=(Ans)%mod;
f[i][j]=Ans;
}
}
cout<<f[n][m]<<'\n';
}
````