$ color{#0066ff}{ 题目描述 }$
九条可怜在玩一个很好玩的策略游戏:Slay the Spire,一开始九条可怜的卡组里有 (2n) 张牌,每张牌上都写着一个数字(w_i),一共有两种类型的牌,每种类型各 (n) 张:
- 攻击牌:打出后对对方造成等于牌上的数字的伤害。
- 强化牌:打出后,假设该强化牌上的数字为(x),则其他剩下的攻击牌的数字都会乘上 (x)。保证强化牌上的数字都大于 1。
现在九条可怜会等概率随机从卡组中抽出 (m) 张牌,由于费用限制,九条可怜最多打出 (k) 张牌,假设九条可怜永远都会采取能造成最多伤害的策略,求她期望造成多少伤害。
假设答案为 ( ext{ans}),你只需要输出
(left ( ext{ans} imes frac{(2n)!}{m!(2n-m)!} ight) ~mod 998244353)
即可
其中 (x!) 表示 (prod_{i=1}^{x}i),特别地,(0!=1)
(color{#0066ff}{输入格式})
第一行一个正整数 (T) 表示数据组数
接下来对于每组数据:
第一行三个正整数 (n,m,k)
第二行 (n) 个正整数 (w_i),表示每张强化牌上的数值。
第三行 (n) 个正整数 (w_i),表示每张攻击牌上的数值。
(color{#0066ff}{输出格式})
输出 (T) 行,每行一个非负整数表示每组数据的答案。
(color{#0066ff}{输入样例})
2
2 3 2
2 3
1 2
10 16 14
2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10
(color{#0066ff}{输出样例})
19
253973805
(color{#0066ff}{数据范围与提示})
对于所有数据,有 (1leq kleq mleq 2nleq 3 imes 10^3),且(1leq w_ileq 10^8)。
保证强化牌上的数字都大于 1。
以下 ((sum 2n)) 表示对于输入中所有数据的(2n)的和。
对于 (10\%) 的数据,有 (1leq sum 2nleq 10)
对于 (20\%) 的数据,有 (1leq sum 2nleq 100)
对于 (30\%) 的数据,有 (1leq sum 2nleq 500)
另有 (20\%) 的数据,满足所有攻击牌的数值相同。
另有 (20\%) 的数据,满足 (m=k)。
对于 (100\%) 的数据,有 (1leq sum 2nleq 30000)
(color{#0066ff}{题解})
假如说我们已经有了m张牌,现在考虑怎么打出k张牌会最优呢
首先,一定是先打强化牌再打攻击牌最优,不解释
那么到底选多少攻击牌和强化牌呢
首先,我们先把m张牌分两类从大到小排序,那么肯定是选两类牌的一个前缀
比如,强化3 2, 攻击a b,(k=3)
那么显然(2*3*a=3a+3a>3a+3b)
因此,我们要尽可能多的选强化牌,剩下的选攻击牌
我们设状态(f[i][j][0/1])表示强化牌中,前i张选j张,第i张选不选(这是为了统计方案不重不漏)的贡献
举个锤子,比如强化牌5 4 3 2,那么(f[4][3][1])就是(2 * 3 * 5+2 * 4 * 5+2 * 3 * 4),就是所有合法贡献的和
同理(g[i][j][0/1])是针对ATK的DP
转移很容易,(O(n^2))
f[0][0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= i; j++) {
if(j >= 1) {
f[i][j][1] = ((f[i - 1][j - 1][1] + f[i - 1][j - 1][0]) % mod * STG[i] % mod);
g[i][j][1] = ((g[i - 1][j - 1][1] + g[i - 1][j - 1][0]) % mod + ATK[i] * C(i - 1, j - 1) % mod) % mod;
}
f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]);
g[i][j][0] = (g[i - 1][j][0] + g[i - 1][j][1]);
}
}
之后我们开始统计方案
为了不重不漏,我们分别在两个序列枚举端点i和j
首先考虑已有的m张牌中,强化牌(ge k-1)张
那么我们肯定是选前(k-1)张大的强化牌和1张攻击牌, 剩下的(m-k)放在其它位置
(f[i][k-1][1]*g[j][1][1]*C_{2n -i-j}^{m-k})
如果没有那么多,只只有(w,w<k-1)张,我们肯定都选,然后剩下的(k-w)张是攻击牌,注意,这时候剩下的(m-k)张牌必须只能是攻击牌, 因为强化牌不够!
(f[i][w][1]*g[j][k-w][1]*C_{n-j}^{m-k})
注意当不选强化牌的时候要特判一下,即(k=1)的时候,还有(k e 1)时不选强化牌的情况,直接统计方案的话因为f是0,所以最后就是0,显然不对了,单独算一下就行
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int mod = 998244353;
const int maxn = 3030;
LL f[maxn][maxn][2], g[maxn][maxn][2];
LL ATK[maxn], STG[maxn], fac[maxn << 1], inv[maxn << 1];
int n, m, k;
LL ksm(LL x, LL y) {
LL re = 1LL;
while(y) {
if(y & 1) re = re * x % mod;
x = x * x % mod;
y >>= 1;
}
return re;
}
LL C(int x, int y) {
if(x < y) return 0;
return fac[x] * inv[y] % mod * inv[x - y] % mod;
}
void predoit() {
std::sort(STG + 1, STG + n + 1, std::greater<LL>());
std::sort(ATK + 1, ATK + n + 1, std::greater<LL>());
f[0][0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= i; j++) {
if(j >= 1) {
f[i][j][1] = ((f[i - 1][j - 1][1] + f[i - 1][j - 1][0]) % mod * STG[i] % mod);
g[i][j][1] = ((g[i - 1][j - 1][1] + g[i - 1][j - 1][0]) % mod + ATK[i] * C(i - 1, j - 1) % mod) % mod;
}
f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]);
g[i][j][0] = (g[i - 1][j][0] + g[i - 1][j][1]);
}
}
}
void fuck() {
LL ans = 0;
for(int i = k - 1; i <= n; i++)
for(int j = 1; j <= n; j++)
(ans += f[i][k - 1][1] * g[j][1][1] % mod * C((n << 1) - i - j, m - k) % mod) %= mod;
for(int w = 1; w <= k - 2; w++) {
LL tot = 0;
for(int i = 1; i <= n; i++) (tot += f[i][w][1]) %= mod;
for(int i = 1; i <= n; i++) (ans += tot * g[i][k - w][1] % mod * C(n - i, m - k) % mod) %= mod;
}
for(int i = 1; i <= n; i++) (ans += g[i][k][1] * C(n - i, m - k) % mod) %= mod;
printf("%lld
", ans);
}
void work() {
LL ans = 0;
for(int i = 1; i <= n; i++) (ans += ATK[i] * C((n << 1) - i, m - k) % mod) %= mod;
printf("%lld
", ans);
}
int main() {
fac[0] = 1;
for(LL i = 1; i <= 6000; i++) fac[i] = i * fac[i - 1] % mod;
inv[6000] = ksm(fac[6000], mod - 2);
for(LL i = 5999; i >= 0; i--) inv[i] = (i + 1) * inv[i + 1] % mod;
for(int T = in(); T --> 0;) {
n = in(), m = in(), k = in();
for(int i = 1; i <= n; i++) STG[i] = in();
for(int i = 1; i <= n; i++) ATK[i] = in();
predoit();
if(k == 1) work();
else fuck();
}
return 0;
}