合法方案数(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; }