合法方案数(dp)


第3题     合法方案数 查看测评数据信息

有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;
}

  

posted @ 2024-07-14 21:18  cn是大帅哥886  阅读(8)  评论(0编辑  收藏  举报