合法方案數(dp)

cn是大帅哥886發表於2024-07-14
https://www.luogu.com.cn/problem/CF1606E

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

  

相關文章