LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)
Update on 1.5
学了 zhou888 的写法,真是又短又快。
并且空间是 \(O(n)\) 的,速度十分优秀。
题意
LOJ #2538. 「PKUWC 2018」Slay the Spire
题解
首先我们考虑拿到一副牌如何打是最优的,不难发现是将强化牌从大到小能打就打,最后再从大到小打攻击牌 。
为什么呢 ?
证明(简单说明) : 如果不是这样 , 那么我们就是有强化牌没有用 , 且攻击牌超过两张 .
我们考虑把最小的那张攻击牌拿出来 , 然后放入一张强化牌 .
\(\because~w_i \ge 2\) 且 最小那张攻击牌的攻击力 \(a_{\min}\) 不大于所有攻击牌的总和 \(a_{sum}\) 的一半
\(\therefore\) 修改后造成的伤害绝对不比前面少 . 得证.
我们只要枚举上下分别用了多少张牌 , 假设 加强 用了 \(a\) 张 , 攻击 用了 \(b\) 张 . \((a + b = m)\)
那么我们只要分两种情况考虑了 :
- \(a < k:\) 那么我们加强可以全用完 , 攻击用前 \(k - a\) 大的 ;
- \(a \ge k:\) 这个加强用前 \(k - 1\) 大的 , 攻击用一张最大的 .
令 \(f_i\) 为选 \(i\) 张强化牌能得到的最优倍率之和,显然强化牌我们从大到小取是最优的。
假设当前取到第 \(j\) 张牌。
那么有如下转移:
\begin{cases}
(f_i + f_{i - 1}) \times a[j] &i < k\\
f_i + f_{i - 1} &i \ge k
\end{cases}
\]
上面那种情况是还能用强化牌,下面已经不能加新的强化牌了,所以不乘上倍率。(注意 \(f_0 = 1\) )
同样我们设 \(g_i\) 为选 \(i\) 张攻击牌能得到的最优攻击之和,此处我们需要从小到大排序。
有如下转移:
\begin{cases}
0 &\le m - (k - 1)\\
g_{i - 1} & >m - (k - 1)
\end{cases}
\]
考虑这张牌我们先放进来,不难发现对于所有 \(i \le m - (k - 1)\) 也就是只能打一张的,我们只统计了这张打的贡献。
如果能打很多张,这样转移的话就能保证我们尽量取的是靠后的那些元素。
最后答案直接就是 \(\displaystyle \sum_{i = 0}^{m} f_{i} g_{m-i}\) 。
总结
需要啥就设啥,想清楚情况再 \(dp\) 。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("2538.in", "r", stdin);
freopen ("2538.out", "w", stdout);
#endif
}
const int N = 3e3 + 1e2, Mod = 998244353;
inline int fpm(int x, int power) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % Mod)
if (power & 1) res = 1ll * res * x % Mod;
return res;
}
int fac[N], ifac[N];
void Math_Init(int maxn) {
fac[0] = ifac[0] = 1;
For (i, 1, maxn) fac[i] = 1ll * fac[i - 1] * i % Mod;
ifac[maxn] = fpm(fac[maxn], Mod - 2);
Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1ll) % Mod;
}
inline int Comb(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
}
int n, m, k, a[N], f[N], g[N];
int main () {
File();
Math_Init(3000);
for (int cases = read(); cases; -- cases) {
n = read(); m = read(); k = read();
For (i, 1, n) a[i] = read();
For (i, 1, max(n, m)) f[i] = g[i] = 0;
sort(a + 1, a + n + 1, greater<int>());
f[0] = 1;
For (i, 1, n) Fordown (j, min(i, m), 0)
if (j <= k - 1) f[j] = (f[j] + 1ll * f[j - 1] * a[i]) % Mod;
else f[j] = (f[j] + f[j - 1]) % Mod;
For (i, 1, n) a[i] = read();
sort(a + 1, a + n + 1);
For (i, 1, n) Fordown (j, min(i, m), 0)
if (j <= m - (k - 1))
g[j] = (g[j] + 1ll * Comb(i - 1, j - 1) * a[i]) % Mod;
else
g[j] = (g[j] + g[j - 1] + 1ll * Comb(i - 1, j - 1) * a[i]) % Mod;
int ans = 0;
For (i, 0, m)
ans = (ans + 1ll * f[i] * g[m - i]) % Mod;
printf ("%d\n", ans);
}
return 0;
}
LOJ #2538. 「PKUWC 2018」Slay the Spire (期望dp)的更多相关文章
- loj2538 「PKUWC 2018」Slay the Spire
pkusc 快到了--做点题涨涨 rp. ref我好菜啊QAQ. 可以发现期望只是一个幌子.我们的目的是:对于所有随机的选择方法(一共 \(\binom{2n}{m}\)种),这些选择方法都最优地打出 ...
- LOJ #2541. 「PKUWC 2018」猎人杀(容斥 , 期望dp , NTT优化)
题意 LOJ #2541. 「PKUWC 2018」猎人杀 题解 一道及其巧妙的题 , 参考了一下这位大佬的博客 ... 令 \(\displaystyle A = \sum_{i=1}^{n} w_ ...
- LOJ #2540. 「PKUWC 2018」随机算法(概率dp)
题意 LOJ #2540. 「PKUWC 2018」随机算法 题解 朴素的就是 \(O(n3^n)\) dp 写了一下有 \(50pts\) ... 大概就是每个点有三个状态 , 考虑了但不在独立集中 ...
- LOJ #2542. 「PKUWC 2018」随机游走(最值反演 + 树上期望dp + FMT)
写在这道题前面 : 网上的一些题解都不讲那个系数是怎么推得真的不良心 TAT (不是每个人都有那么厉害啊 , 我好菜啊) 而且 LOJ 过的代码千篇一律 ... 那个系数根本看不出来是什么啊 TAT ...
- LOJ #2537. 「PKUWC 2018」Minimax (线段树合并 优化dp)
题意 小 \(C\) 有一棵 \(n\) 个结点的有根树,根是 \(1\) 号结点,且每个结点最多有两个子结点. 定义结点 \(x\) 的权值为: 1.若 \(x\) 没有子结点,那么它的权值会在输入 ...
- LOJ #2802. 「CCC 2018」平衡树(整除分块 + dp)
题面 LOJ #2802. 「CCC 2018」平衡树 题面有点难看...请认真阅读理解题意. 转化后就是,给你一个数 \(N\) ,每次选择一个 \(k \in [2, N]\) 将 \(N\) 变 ...
- loj 2778「BalticOI 2018」基因工程
loj luogu 这题和NOI那道向量内积一个套路 首先考虑求两行的不同元素个数,可以转化成一个行向量\(a\)和列向量\(b\)相乘得到一个值.如果只有\(A,C\)两种字符,那么令对应权值\(A ...
- 「PKUWC 2018」Minimax
传送门:Here 一道线段树合并好题 如果要维护点$ x$的信息,相当于合并$ x$的两棵子树 对于这题显然有:任何叶子节点的权值都可能出现在其祖先上 因而我们只需要在线段树合并的时候维护概率即可 我 ...
- loj2540 「PKUWC 2018」随机算法
pkusc 快到了--做点题涨涨 rp. 记 \(f(S,i)\) 表示 \(S\) 这个集合是决计不能选的(要么属于独立集,要么和独立集相连),或称已经考虑了的,\(i\) 表示此集合对应的最大独立 ...
随机推荐
- Hive 创建表
创建表的三种方式: 方式一:新建表结构 CREATE TABLE emp( empno int, ename string ) ROW FORMAT DELIMITED FIELDS TERMINAT ...
- UVA11255 Necklace Burnside、组合
VJ传送门 因为有每种颜色个数的限制,所以不能使用Polya 考虑退一步,使用Burnside引理求解 回忆一下Burnside引理,它需要求的是置换群中每一个置换的不动点个数,也就是施加一次置换之后 ...
- 如何从现有版本1.4.8升级到element UI2.0.11
现在的项目是定死的依赖以下几个核心组件的版本: vue 2.3.3 element-ui 1.4.8 vue-template-comiler 2.3.3 将以前定死的依赖修改为 vue ^2.3.3 ...
- 事件(event)
事件概述 委托是一种类型可以被实例化,而事件可以看作将多播委托进行封装的一个对象成员(简化委托调用列表增加和删除方法)但并非特殊的委托,保护订阅互不影响. 基础事件(event) 在.Net中声明事件 ...
- (4)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Consul服务发现和消费
上一章说了 Consul服务注册 现在我要连接上Consul里面的服务 请求它们的API接口 应该怎么做呢? 1.找Consul要一台你需要的服务器 1.1 获取Consul下的所有注册的服务 u ...
- SNMP基础知识
注:本篇博客并非原创,仅是学习笔记 1. 概述1.1 诞生背景1.2 SNMP简介1.3 版本1.4 术语1.5 网络结构1.6 MIB简介2. Linux的SNMP安装 1. 概述 1.1 诞生背景 ...
- Ionic 入门与实战之第一章:Ionic 介绍与相关学习资源
原文发表于我的技术博客 本文是「Ionic 入门与实战」系列连载的第一章,主要对 Ionic 的概念.发展历程.适配的移动平台等知识进行了介绍,并分享了 Ionic 相关的学习资源. 原文发表于我的技 ...
- 深入浅出Automation Anywhere
Automation Anywhere是基于CLIENT-SERVER架构(control room和客户端),客户端主要是Bot Creator 和 BotRunner 主要构成: 1.WEBCR: ...
- 系统、决策、控制研究系列(SSDC)
本类目主要介绍的书籍来自springer的系列书籍中的一本,对于该系列书籍介绍如下: “系统.决策及控制研究”(SSDC)系列涵盖了在广泛认知的系统.决策及控制的各个领域的快速.最新和高质量的最新发展 ...
- MFS+Keepalived双机高可用热备方案操作记录
基于MFS的单点及手动备份的缺陷,考虑将其与Keepalived相结合以提高可用性.在Centos下MooseFS(MFS)分布式存储共享环境部署记录这篇文档部署环境的基础上,只需要做如下改动: 1) ...