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