题意

\(n\) 个人,他们要进行下面的进程:每轮设存活 \(i\) 个人,那么每个人会减少 \(i-1\) 的血量,血量小于等于零就会被淘汰,现在需要你给他们每个人设置一个在 \([1,x]\) 之间的初始血量,使得某轮游戏结束后,无人生还,求这样的方案数。

分析

考虑 dp 处理,以 \(f[i,j]\) 表示还存活 \(i\) 个人,且他们都已经受到 \(j\) 点伤害,已被淘汰的人的血量的方案数。因此我们每次考虑转移时,向目标转移的倍数一定是由本次决策淘汰的人决定的,容易知道,当枚举到 \(f[i,j]\) 作为被转移的值时,目标的第二维一定是 \(\min(j+i-1,x)\),所以只需枚举第一维,所以时间复杂度是 \(O(n^3)\)

然后本次被淘汰的人的血量范围为 \([j+1,i+j-1]\)。因为题目中说了每个人各不相同,所以还要乘上组合数,从 \(i\) 个人中选 \(k\) 个人存活,再乘上 \(f[i,j]\) 即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
inline void read(int &res){
	char c;
	int f=1;
	res=0;
	c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
	res*=f;
}
int n,x;
int f[505][505];
int c[505][505];
inline int qpow(int ds,int zs){
	int ans=1;
	while(zs){
		if(zs&1)ans=ans*ds%mod;
		ds=ds*ds%mod;zs>>=1;
	}
	return ans;
}
signed main()
{
	read(n);read(x);
	c[0][0]=1;
	for(int i=1;i<=500;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;//预处理组合数 
	}
	f[n][0]=1;
	for(int i=n;i>=2;i--){//不枚举1,不合法 
		for(int j=0;j<=x;j++){
			if(!f[i][j])continue;
			if(j>=x)continue;//小剪枝 
			for(int k=i;k>=0;k--){
				f[k][min(i+j-1,x)]+=f[i][j]*c[i][k]%mod*qpow(min(i+j-1,x)-j,i-k)%mod;//转移 
				if(f[k][min(i+j-1,x)]>=mod)f[k][min(i+j-1,x)]-=mod;
			}
		}
	}
	int ans=0;
	for(int i=0;i<=x;i++){
		ans=(ans+f[0][i])%mod;
	}
	cout<<ans;
	return 0;
}


posted on 2021-11-12 16:18  漠寒·  阅读(118)  评论(0编辑  收藏  举报