题解:Arena
题意分析
有 \(n\) 个人,第 \(i\) 个人的血量为 \(a_i\),每一轮每一个存活的人都会让其他所有人的血量减 \(1\),血量小于等于 \(0\) 死亡。
给出人数 \(n\) 和 \(x\),满足 \(a_i\leq x\)(\(a_i\) 最大值不一定为 \(x\)),求 \(a_i\) 可能的方案数,对 \(998244353\) 取模。
考虑 DP。
状态设计
令 \(dp_{i,j}\) 表示 \(i\) 个人存活,存活的人中最大血量为 \(j\) 的方案数。
状态转移
有两种情况:
-
这一轮后,所有人都死了,即 \(j\leq i-1\)。
那么所有情况总数即最大血量为 \(j\) 的总情况数,即最大血量小于等于 \(j\) 的方案数减去小于等于 \(j-1\) 的方案数,即:
\[dp_{i,j}=j^i-(j-1)^i \] -
这一轮后,有人存活,即 \(j>i-1\)。
则所有人的血量都减去了 \(i-1\),最大血量从 \(j\) 变为 \(j-i+1\),方案数为:
\[dp_{i,j}=\sum_{k=1}^i\binom{i}{k}dp_{k,j-i+1}(i-1)^{i-k} \]即从 \(i\) 个人中选择 \(k\) 个人存活,方案数为 \(\dbinom{i}{k}dp_{k,j-i+1}\),剩余 \(i-k\) 个死人只需要满足原来的最大血量不大于 \(i-1\) 即可,方案数为 \((i-1)^{i-k}\)。
AC 代码
快速幂的时候写一个记忆化,可以把时间复杂度从 \(\mathcal O\left(n^2x\log n\right)\) 优化为 \(\mathcal O\left(n^2x\right)\)。
不写记忆化则需要卡常(非递归式快速幂)。
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=500,X=500,P=998244353;
int dp[N+1][X+1],c[N+1][N+1];
int qpow(int a,int n){
static int mem[N+1][N+1];
int &tmp=mem[a][n];
if(tmp){
return tmp;
}
int t=1;
while(n){
if(n&1){
t=1ll*t*a%P;
}
a=1ll*a*a%P;
n>>=1;
}
return tmp=t;
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
int n,x;
scanf("%d %d",&n,&x);
for(int i=0;i<=n;i++){
c[i][0]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=x;j++){
if(i-1>=j){
dp[i][j]=(qpow(j,i)-qpow(j-1,i))%P;
}else{
for(int k=1;k<=i;k++){
dp[i][j]=(dp[i][j]+1ll*c[i][k]*dp[k][j-i+1]%P*qpow(i-1,i-k)%P)%P;
}
}
}
}
int ans=0;
for(int i=1;i<=x;i++){
ans=(1ll*ans+dp[n][i])%P;
}
printf("%d\n",(ans<0?ans+P:ans));
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号