合法方案数(dp)
有n个人,他们要进行下面的进程:每轮设存活i个人,那么每个存活的人会对其他人造成1点血量的代价,血量小于等于零就会被淘汰,现在需要你给他们每个人设置一个在[1,x]之间的初始血量,使得某轮游戏结束后,无人生还,求这样的方案数。对998244353取模
输入格式
第一行两个整数n和x
2<=n<=500,1<=x<=500
输出格式
一个整数
输入/输出例子1
输入:
2 5
输出:
5
样例解释
无
定dp状态时,不知道每个值的具体数,但又需要用到,可以定为这一堆值中的最大值是多少,再慢慢推!
又一道dp好题
这题看着是很迷糊的,但是看到方案数应该要想到dp。考虑到n的范围较小,可以选用二维到三维。我们先考虑二维,不行再补一个维
定f(i, j)表示: 现在有i个人活着,且这i个人最大的那个人的血量j
这是根据题目中两个关键变量定的,第二维就很奇妙,几乎涵盖每个人的血量
然后分类转移。
由题目知,每个人会扣i-1的血量,那我们讨论这一堆人是死光还是没死光
死光:
i-1>=j,很显然是这个条件,具体转移方程呢?
考虑用减法原理(正面不好想)转化为,全部方案数-最大血量不为j的数量,就求出了最大血量为j的方案数
全部方案数:每个人最多j点血量,共i人,全部方案数就为 j^i
最大血量不为j:每个人最多j-1点血量,共i人,全部方案数就为 (j-1)^i
总结就是 f(i, j)=j^i-(j-1)^i
没死光:
就是死光的反过来,i-1<j
没死光可以讨论部分人活着,部分人死了,最后他俩相乘即可
设活着k人。那么考虑这k个人分别在哪个位置,以及他们的最大血量(j减去i-1嘛)
活着方案数:C(i, k) * f(k, j-(i-1))
死了的人,死前最大血量容易得出是i-1,即为扣除血量,有i-k人死了,就推出以下式子(因为活着的人位置已经选过了,那死的人的位置自然确定好了)
死了方案数: (i-1)^(i-k)
总结就是 f(i, j)=C(k, i) * f(k, j-(i-1)) * (i-1)^(i-k)
#include <bits/stdc++.h>
using namespace std;
const int N=505, Mod=998244353;
int n, x;
long long ans=0, f[N][N], yh[N][N];
long long Pow(long long a, long long b)
{
long long sum=1;
while (b)
{
if (b&1) sum=sum%Mod*a%Mod;
b>>=1;
a=a*a%Mod;
}
return sum;
}
void yanghui()
{
for (int i=1; i<504; i++)
{
yh[i][1]=yh[i][i]=1;
for (int j=2; j<i; j++)
{
yh[i][j]=(yh[i-1][j]+yh[i-1][j-1])%Mod;
}
}
}
long long C(int n, int m)
{
return yh[n+1][m+1];
}
int main()
{
yanghui();
scanf("%d%d", &n, &x);
for (int i=2; i<=n; i++)
{
for (int j=1; j<=x; j++)
{
if (i-1>=j) f[i][j]=(Pow(j, i)-Pow(j-1, i)+Mod)%Mod;
else
{
for (int k=1; k<=i; k++)
{
f[i][j]=(f[i][j]+((C(i, k)*f[k][j-(i-1)])%Mod*Pow(i-1, i-k))%Mod)%Mod;
}
}
}
}
for (int i=1; i<=x; i++) ans=(ans+f[n][i])%Mod;
printf("%lld", ans);
return 0;
}

浙公网安备 33010602011771号