LOJ2538. 「PKUWC2018」Slay the Spire【組合數學】
阿新 • • 發佈:2018-12-14
思路
首先因為式子後面把方案數乘上了
所以其實只用輸出所有方案的攻擊力總和
然後很顯然可以用強化牌就儘量用
因為每次強化至少把下面的牌翻一倍,肯定是更優的
然後就只有兩種情況
- 強化牌數量少於k
- 強化牌數量大於等於k
根據乘法原理,設\(f_{i,j}\)是選i張強化牌用j張的倍數總和,\(g_{i,j}\)是選i張攻擊用j張的倍數總和
\(ans+=f_{k,k}*g_{m-i,m-k}\)
\(ans+=f_{i,k-1}*g_{m-i,1}\)
然後f的計算可以量化大小這個東西,就是先排序
dp出選了i個數,最後一個在j的方案數,這樣前面的j各種不可能選出其他數,對於後面的數直接組合數計算就可以了
#include<bits/stdc++.h> using namespace std; const int Mod = 998244353; const int N = 3e3 + 10; int n, m, k, a[N], b[N], c[N][N]; int sum[N], f[N][N], g[N][N]; int add(int a, int b) { return (a += b) >= Mod ? a - Mod : a; } int mul(int a, int b) { return 1ll * a * b % Mod; } void init() { for (int i = 0; i < N; i++) c[i][0] = 1; for (int i = 1; i < N; i++) { for (int j = 1; j <= i; j++) { c[i][j] = add(c[i - 1][j], c[i - 1][j - 1]); } } } int calcf(int a, int b) { // 取a張用b張 if (a < b) return 0; if (!b) return c[n][a]; //** int res = 0; for (int i = 1; i <= n; i++) res = add(res, mul(f[b][i], c[n - i][a - b])); return res; } int calcg(int a, int b) { if (a < b) return 0; if (!b) return 0; //** int res = 0; for (int i = 1; i <= n; i++) res = add(res, mul(g[b][i], c[n - i][a - b])); return res; } void solve() { scanf("%d %d %d", &n, &m, &k); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) scanf("%d", &b[i]); sort(a + 1, a + n + 1, [&](const int a, const int b) {return a > b;}); sort(b + 1, b + n + 1, [&](const int a, const int b) {return a > b;}); for (int i = 1; i <= n; i++) { f[1][i] = a[i]; sum[i] = add(sum[i - 1], a[i]); } for (int i = 2; i <= n; i++) { for (int j = i; j <= n; j++) f[i][j] = mul(sum[j - 1], a[j]); for (int j = 1; j <= n; j++) sum[j] = add(sum[j - 1], f[i][j]); } for (int i = 1; i <= n; i++) { g[1][i] = b[i]; sum[i] = add(sum[i - 1], b[i]); } for (int i = 2; i <= n; i++) { for (int j = i; j <= n; j++) { g[i][j] = add(mul(b[j], c[j - 1][i - 1]), sum[j - 1]); } for (int j = 1; j <= n; j++) sum[j] = add(sum[j - 1], g[i][j]); } int ans = 0; for (int i = max(0, m - n); i <= min(n, m); i++) { if (i < k) ans = add(ans, mul(calcf(i, i), calcg(m - i, k - i))); else ans = add(ans, mul(calcf(i, k - 1), calcg(m - i, 1))); } printf("%d\n", ans); } int main() { #ifdef dream_maker freopen("input.txt", "r", stdin); #endif init(); int T; scanf("%d", &T); while (T--) solve(); return 0; }