1. 程式人生 > >LOJ2538 PKUWC2018 Slay the Spire DP

LOJ2538 PKUWC2018 Slay the Spire DP

tps strong 合數 size ont 傳送門 int 攻擊 前綴

傳送門

不想放題面了,咕咕咕咕咕


這個期望明明是用來嚇人的,其實要算的就是所有方案的最多傷害的和。

首先可以知道的是,能出強化牌就出強化牌(當然最後要留一張攻擊牌出出去),且數字盡量大

所以說在強化牌數量$< K$時會打出所有強化牌和剩下的最大的攻擊牌,而強化牌數量$\geq K$的時候則會打出$K-1$張強化牌和$1$張攻擊牌,且它們的數字都是最大的

我們不妨計算每一種最優打出的方案存在在多少種抽取方案中。

設$f_{i,j}$表示使用$i$張強化牌,其中數值最小的牌是第$j$張時的方案的強化數值之和,$g_{i,j}$表示使用$i$張攻擊牌,其中數值最小的牌是第$j$張時的方案的攻擊數值之和,簡單的前綴和優化DP就可以完成。處理完之後,所有抽取了$x$張強化牌和$y$張攻擊牌,選擇$i$張強化牌和$j$張攻擊牌的方案的總傷害和就是

$$\sum\limits_k \sum\limits_l f_{i,k} \times g_{j,l} \times C_{k-1}^{x-i} \times C_{l-1}^{y-j}$$

兩個理解:

①$f$和$g$中間用乘號是因為$f$中間的每一個方案和$g$中的每一個方案都可以對應產生一種抽取方式

②後面的兩個組合數的意思是:對於強化牌,已經使用了$i$張,最小的是$k$,那麽剩下的$x-i$張需要在剩余的$k-1$中抽取,方案數就是組合數,後面同理

 1 #include<bits/stdc++.h>
 2 //This code is written by Itst
 3
using namespace std; 4 5 inline int read(){ 6 int a = 0; 7 bool f = 0; 8 char c = getchar(); 9 while(c != EOF && !isdigit(c)){ 10 if(c == -) 11 f = 1; 12 c = getchar(); 13 } 14 while(c != EOF && isdigit(c)){ 15 a = (a << 3
) + (a << 1) + (c ^ 0); 16 c = getchar(); 17 } 18 return f ? -a : a; 19 } 20 21 const int MOD = 998244353 , MAXN = 3000; 22 long long dp[MAXN + 10][MAXN + 10] , f[MAXN + 10][MAXN + 10] , g[MAXN + 10][MAXN + 10] , num[2][MAXN + 10] , jc[MAXN + 10] , ny[MAXN + 10] , N , M , K , ans; 23 24 bool cmp(int a , int b){ 25 return a > b; 26 } 27 28 inline int poww(long long a , int b){ 29 int times = 1; 30 while(b){ 31 if(b & 1) 32 times = times * a % MOD; 33 a = a * a % MOD; 34 b >>= 1; 35 } 36 return times; 37 } 38 39 signed main(){ 40 #ifndef ONLINE_JUDGE 41 freopen("2538.in" , "r" , stdin); 42 //freopen("2538.out" , "w" , stdout); 43 #endif 44 jc[0] = 1; 45 for(long long i = 1 ; i <= MAXN ; ++i) 46 jc[i] = jc[i - 1] * i % MOD; 47 ny[MAXN] = poww(jc[MAXN] , MOD - 2); 48 for(long long i = MAXN - 1 ; i >= 0 ; --i) 49 ny[i] = ny[i + 1] * (i + 1) % MOD; 50 for(int i = 0 ; i <= MAXN ; ++i) 51 dp[0][i] = 1; 52 for(int i = 1 ; i <= MAXN ; ++i) 53 for(int j = 1 ; j <= MAXN ; ++j) 54 dp[i][j] = (dp[i - 1][j - 1] + dp[i][j - 1]) % MOD; 55 for(int i = 0 ; i <= MAXN ; ++i) 56 f[0][i] = 1; 57 58 for(int T = read() ; T ; --T){ 59 N = read(); 60 M = read(); 61 K = read(); 62 for(int i = 1 ; i <= N ; ++i) 63 num[0][i] = read(); 64 sort(num[0] + 1 , num[0] + N + 1 , cmp); 65 for(int i = 1 ; i <= N ; ++i) 66 num[1][i] = read(); 67 sort(num[1] + 1 , num[1] + N + 1 , cmp); 68 ans = 0; 69 70 for(int i = 1 ; i <= N ; ++i) 71 for(int j = 1 ; j <= N ; ++j){ 72 f[i][j] = (1ll * f[i - 1][j - 1] * num[0][j] + f[i][j - 1]) % MOD; 73 g[i][j] = (g[i - 1][j - 1] + 1ll * (dp[i][j] - dp[i][j - 1] + MOD) * num[1][j] + g[i][j - 1]) % MOD; 74 } 75 76 for(int i = 0 ; i < K ; ++i){ 77 int sum1 = f[i][N] , sum2 = 0; 78 for(int j = K - i ; N - j >= M - K ; ++j) 79 sum2 = (sum2 + 1ll * (g[K - i][j] - g[K - i][j - 1] + MOD) * jc[N - j] % MOD * ny[M - K] % MOD * ny[N - j - (M - K)]) % MOD; 80 ans = (ans + 1ll * sum1 * sum2) % MOD; 81 } 82 83 for(int i = K ; i < M ; ++i){ 84 int sum1 = 0 , sum2 = 0; 85 for(int j = K - 1 ; N - j >= i - (K - 1) ; ++j) 86 sum1 = (sum1 + 1ll * (f[K - 1][j] - f[K - 1][j - 1] + MOD) * jc[N - j] % MOD * ny[i - (K - 1)] % MOD * ny[N - j - (i - (K - 1))]) % MOD; 87 for(int j = 1 ; N - j >= M - i - 1 ; ++j) 88 sum2 = (sum2 + 1ll * (g[1][j] - g[1][j - 1] + MOD) * jc[N - j] % MOD * ny[M - i - 1] % MOD * ny[N - j - (M - i - 1)]) % MOD; 89 ans = (ans + 1ll * sum1 * sum2) % MOD; 90 } 91 cout << ans << endl; 92 } 93 return 0; 94 }

LOJ2538 PKUWC2018 Slay the Spire DP