$ \color{#0066ff}{ 题目描述 }$

九条可怜在玩一个很好玩的策略游戏:Slay the Spire,一开始九条可怜的卡组里有 \(2n\) 张牌,每张牌上都写着一个数字\(w_i\),一共有两种类型的牌,每种类型各 \(n\) 张:

  1. 攻击牌:打出后对对方造成等于牌上的数字的伤害。
  2. 强化牌:打出后,假设该强化牌上的数字为\(x\),则其他剩下的攻击牌的数字都会乘上 \(x\)。保证强化牌上的数字都大于 1

现在九条可怜会等概率随机从卡组中抽出 \(m\) 张牌,由于费用限制,九条可怜最多打出 \(k\) 张牌,假设九条可怜永远都会采取能造成最多伤害的策略,求她期望造成多少伤害。

假设答案为 \(\text{ans}\),你只需要输出

\(\left (\text{ans}\times \frac{(2n)!}{m!(2n-m)!}\right) ~\bmod 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}{输入样例}\)

  1. 2
  2. 2 3 2
  3. 2 3
  4. 1 2
  5. 10 16 14
  6. 2 3 4 5 6 7 8 9 10 11
  7. 1 2 3 4 5 6 7 8 9 10

\(\color{#0066ff}{输出样例}\)

  1. 19
  2. 253973805

\(\color{#0066ff}{数据范围与提示}\)

对于所有数据,有 \(1\leq k\leq m\leq 2n\leq 3\times 10^3\),且\(1\leq w_i\leq 10^8\)。

保证强化牌上的数字都大于 1

以下 \((\sum 2n)\) 表示对于输入中所有数据的\(2n\)的和。

对于 \(10\%\) 的数据,有 \(1\leq \sum 2n\leq 10\)

对于 \(20\%\) 的数据,有 \(1\leq \sum 2n\leq 100\)

对于 \(30\%\) 的数据,有 \(1\leq \sum 2n\leq 500\)

另有 \(20\%\) 的数据,满足所有攻击牌的数值相同。

另有 \(20\%\) 的数据,满足 \(m=k\)。

对于 \(100\%\) 的数据,有 \(1\leq \sum 2n\leq 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)\)

  1. f[0][0][0] = 1;
  2. for(int i = 1; i <= n; i++) {
  3. for(int j = 0; j <= i; j++) {
  4. if(j >= 1) {
  5. f[i][j][1] = ((f[i - 1][j - 1][1] + f[i - 1][j - 1][0]) % mod * STG[i] % mod);
  6. 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;
  7. }
  8. f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]);
  9. g[i][j][0] = (g[i - 1][j][0] + g[i - 1][j][1]);
  10. }
  11. }

之后我们开始统计方案

为了不重不漏,我们分别在两个序列枚举端点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\ne 1\)时不选强化牌的情况,直接统计方案的话因为f是0,所以最后就是0,显然不对了,单独算一下就行

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. LL in() {
  4. char ch; LL x = 0, f = 1;
  5. while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
  6. for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
  7. return x * f;
  8. }
  9. const int mod = 998244353;
  10. const int maxn = 3030;
  11. LL f[maxn][maxn][2], g[maxn][maxn][2];
  12. LL ATK[maxn], STG[maxn], fac[maxn << 1], inv[maxn << 1];
  13. int n, m, k;
  14. LL ksm(LL x, LL y) {
  15. LL re = 1LL;
  16. while(y) {
  17. if(y & 1) re = re * x % mod;
  18. x = x * x % mod;
  19. y >>= 1;
  20. }
  21. return re;
  22. }
  23. LL C(int x, int y) {
  24. if(x < y) return 0;
  25. return fac[x] * inv[y] % mod * inv[x - y] % mod;
  26. }
  27. void predoit() {
  28. std::sort(STG + 1, STG + n + 1, std::greater<LL>());
  29. std::sort(ATK + 1, ATK + n + 1, std::greater<LL>());
  30. f[0][0][0] = 1;
  31. for(int i = 1; i <= n; i++) {
  32. for(int j = 0; j <= i; j++) {
  33. if(j >= 1) {
  34. f[i][j][1] = ((f[i - 1][j - 1][1] + f[i - 1][j - 1][0]) % mod * STG[i] % mod);
  35. 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;
  36. }
  37. f[i][j][0] = (f[i - 1][j][0] + f[i - 1][j][1]);
  38. g[i][j][0] = (g[i - 1][j][0] + g[i - 1][j][1]);
  39. }
  40. }
  41. }
  42. void fuck() {
  43. LL ans = 0;
  44. for(int i = k - 1; i <= n; i++)
  45. for(int j = 1; j <= n; j++)
  46. (ans += f[i][k - 1][1] * g[j][1][1] % mod * C((n << 1) - i - j, m - k) % mod) %= mod;
  47. for(int w = 1; w <= k - 2; w++) {
  48. LL tot = 0;
  49. for(int i = 1; i <= n; i++) (tot += f[i][w][1]) %= mod;
  50. for(int i = 1; i <= n; i++) (ans += tot * g[i][k - w][1] % mod * C(n - i, m - k) % mod) %= mod;
  51. }
  52. for(int i = 1; i <= n; i++) (ans += g[i][k][1] * C(n - i, m - k) % mod) %= mod;
  53. printf("%lld\n", ans);
  54. }
  55. void work() {
  56. LL ans = 0;
  57. for(int i = 1; i <= n; i++) (ans += ATK[i] * C((n << 1) - i, m - k) % mod) %= mod;
  58. printf("%lld\n", ans);
  59. }
  60. int main() {
  61. fac[0] = 1;
  62. for(LL i = 1; i <= 6000; i++) fac[i] = i * fac[i - 1] % mod;
  63. inv[6000] = ksm(fac[6000], mod - 2);
  64. for(LL i = 5999; i >= 0; i--) inv[i] = (i + 1) * inv[i + 1] % mod;
  65. for(int T = in(); T --> 0;) {
  66. n = in(), m = in(), k = in();
  67. for(int i = 1; i <= n; i++) STG[i] = in();
  68. for(int i = 1; i <= n; i++) ATK[i] = in();
  69. predoit();
  70. if(k == 1) work();
  71. else fuck();
  72. }
  73. return 0;
  74. }

loj #2538. 「PKUWC2018」Slay the Spire的更多相关文章

  1. 【LOJ】#2538. 「PKUWC2018」Slay the Spire

    题解 由于强化卡都是大于1的,我们分析一下就会发现,尽可能多的用强化卡,至少用一张攻击卡,一定是每组卡牌的最优选择 所以我们把攻击卡和强化卡从大到小排序 我们设\(g[i][j]\)表示前i张卡牌里选 ...

  2. LOJ2538. 「PKUWC2018」Slay the Spire【组合数学】

    LINK 思路 首先因为式子后面把方案数乘上了 所以其实只用输出所有方案的攻击力总和 然后很显然可以用强化牌就尽量用 因为每次强化至少把下面的牌翻一倍,肯定是更优的 然后就只有两种情况 强化牌数量少于 ...

  3. loj2538 「PKUWC2018」Slay the Spire 【dp】

    题目链接 loj2538 题解 比较明显的是,由于强化牌倍数大于\(1\),肯定是能用强化牌尽量用强化牌 如果强化牌大于等于\(k\),就留一个位给攻击牌 所以我们将两种牌分别排序,企图计算\(F(i ...

  4. 「PKUWC2018」Slay the Spire

    题目链接 题意分析 这个题其实不是期望 就是一共有\(C_{2n}^m\)种情况 每一种情况选择\(k\)张牌 然后求最大攻击值的总和 我们考虑 当前抽出了选出了\(i\)张强化牌 \(m-i\)张攻 ...

  5. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  6. loj#2537. 「PKUWC2018」Minimax

    题目链接 loj#2537. 「PKUWC2018」Minimax 题解 设\(f_{u,i}\)表示选取i的概率,l为u的左子节点,r为u的子节点 $f_{u,i} = f_{l,i}(p \sum ...

  7. LOJ #2541「PKUWC2018」猎人杀

    这样$ PKUWC$就只差一道斗地主了 假装补题补完了吧..... 这题还是挺巧妙的啊...... LOJ # 2541 题意 每个人有一个嘲讽值$a_i$,每次杀死一个人,杀死某人的概率为$ \fr ...

  8. LOJ #2542「PKUWC2018」随机游走

    $ Min$-$Max$容斥真好用 $ PKUWC$滚粗后这题一直在$ todolist$里 今天才补掉..还要更加努力啊.. LOJ #2542 题意:给一棵不超过$ 18$个节点的树,$ 5000 ...

  9. LOJ 2542 「PKUWC2018」随机游走 ——树上高斯消元(期望DP)+最值反演+fmt

    题目:https://loj.ac/problem/2542 可以最值反演.注意 min 不是独立地算从根走到每个点的最小值,在点集里取 min ,而是整体来看,“从根开始走到点集中的任意一个点就停下 ...

随机推荐

  1. php SqlServer 中文汉字乱码

    php SqlServer 中文汉字乱码,用iconv函数转换 查询显示的时候,从GB转换为UTF8 <?php echo iconv('GB2312','UTF-8',$row['Name'] ...

  2. 【281】◀▶ arcpy.mapping 常用类说明

    arcpy.mapping 教程入门 arcpy.mapping 指导原则 按字母顺序排序的 arcpy.mpping 类列表 按字母顺序排序的 arcpy.mpping 函数列表 按字母顺序排序的 ...

  3. mysql语句规范

  4. 【FZU 2277】Change

    题意 有一颗有n个节点的有根树,根节点编号时1,每个结点都有一个值ai,开始的时候,所有节点的值都是0. 我们有q个操作,操作只有两种类型 1 v x k,a[v]+=x,a[v']+=x-k,a[v ...

  5. 【bzoj2144】跳跳棋

    2144: 跳跳棋 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 492  Solved: 244[Submit][Status][Discuss] ...

  6. laravel 验证机制validation

    Laravel 中 validation 验证 返回中文提示 全局设置 自己建一个zn文件夹,然后把en的4个文件全复制过去,修改validation.php的代码为下面的内容,然后在app.php修 ...

  7. 使用python创建生成动态链接库dll

    如今,随着深度学习的发展,python已经成为了深度学习研究中第一语言.绝大部分的深度学习工具包都有python的版本,很多重要算法都有python版本的实现.为了将这些算法应用到具体工程中,这些工具 ...

  8. Angular24 树形菜单 ???

    待更新... 2018年5月21日15:17:47 参考博文01 参考博文02

  9. jq获取table总行数

    var rows = $('table').find("tr").length;

  10. 实践作业3:白盒测试----findbugs介绍及使用DAY7

    本小组选择的是一个开源的Java静态代码分析工具----Findbugs. 与其他静态分析工具(如Checkstyle和PMD)不同,FindBugs 不注重样式或者格式,它专注于寻找真正的缺陷或者潜 ...