安吉D8-E
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E本文
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
P10381 杂赛选比
题目背景
你说得对,但是小 在打 CF 时将 Earn or Unlock 错看成了下面的鬼畜样子,痛失 2h 遗憾离场,希望大家引以为戒.
题目描述
给定一个长度为 的数组 ,初始只有 是已被解锁的.现在有一个整数 ,初始值为 .现在小 在对这个数组进行一个游戏:
- 如果 未被解锁,游戏结束.
- 否则他可以将 设置成已被解锁的,或是获得 个金币(如果 则无法解锁任何元素),然后将 加 .
请你求出游戏结束后你能获得的最大金币数量.
输入格式
本题有多组测试数据.
第一行一个整数 ,表示测试数据组数.
对于每一组数据,第一行一个正整数 .
接下来一行 个非负整数 .
输出格式
对于每一组数据,一行一个数,表示答案.
输入输出样例 #1
输入 #1
321 252 4 5 0 140 4 4 4输出 #1
290输入输出样例 #2
输入 #2
1101 1 4 5 1 4 1 9 1 9输出 #2
26说明/提示
【样例 1 解释】
对于第一组数据,你可以解锁 ,再获得 个金币.而对于第三组数据,你无法解锁 ,因此只能获得 个金币.
对于第二组数据,你可以解锁 ,并获得 个金币.
【样例 2 解释】
将第 个位置用于解锁为最优方案.
【数据范围】
对于 的数据,,,.
| 测试点编号 | 特殊性质 | |||
|---|---|---|---|---|
| / | ||||
| / | ||||
| / | ||||
| / | ||||
| / | ||||
| / |
此题考虑使用动态规划.
不妨定义 dp[i][0/1] 表示考虑到第 张卡牌(即 数组中第 个数),且第 张选择用于解锁(第二维为 0)或选择用于获得金币(第二维为 1),所能够获得的最大金币.
显然,当想要去考虑第 张卡牌,前提是第 张卡牌被解锁.因此,dp[i][0/1] 可以由所有能够解锁第 张卡牌的状态转移而来.具体来说,是由所有满足以下条件的卡牌 从 dp[k][0] 转移而来:
- 卡牌 的解锁范围能够触及到卡牌 ,即 .
由于使用卡牌 之后,卡牌 立即被解锁了,因此处于卡牌 到 之间的卡牌选择什么都可以,显然,这里选择得分比选择解锁更优,因此,可以在转移时加上卡牌 的权值之和,因此,有如下的转移方程:
其中的 满足上述条件.最终状态即为 .
不难发现,dp[x][1]()没有在转移中提供过数据,因此可以舍去第二维,且默认第二维是 0.这样状态就变为了:考虑前 张卡牌,且钦定第 张需要用于解锁,所能够获取的最多金币.这样最终状态需要补上最后一张卡牌用于解锁相对用来获取金币所带来的损失(即 ),故最终状态为 .
求区间和可以使用前缀和优化,总算法时间复杂度为 .
40pts 标程
#include <bits/stdc++.h>#define sum(l, r) (s[(r)] - s[(l) - 1])using namespace std;const int N = 1e5 + 100;int n, a[N], f[N], s[N];
void solve() { cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; s[i] = s[i - 1] + a[i]; f[i] = -1e8; } f[1] = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j < i; j++) { if (j + a[j] >= i) { f[i] = max(f[i], f[j] + sum(j + 1, i - 1)); } } } int ans = a[1] + f[1]; for (int i = 2; i <= n; i++) { ans = max(ans, a[i] + f[i]); } cout << ans << endl;}
signed main() { int T; cin >> T; while (T--) solve(); return 0;}考虑如何优化掉枚举 的那一层循环.
上面的转移方程可以变形为下面的形式:
若设 ,则可以表示为:
可以使用线段树动态维护最大值.每计算完一个点 之后,将 对应的 当作值, 当作键加入线段树.在每一次计算时,查询在 之间的元素的最大值,就可以使用 的时间复杂度计算 .
标程
#include <bits/stdc++.h>#define int long longusing namespace std;const int N = 1e5 + 100;const int INF = 1e18;int n, a[N], f[N], s[N];
struct SegmentTree { struct Node { int l, r, max; friend Node operator+(const Node a, const Node b) { if (a.max == -INF) return b; if (b.max == -INF) return a; Node nNode; nNode.max = std::max(a.max, b.max); nNode.l = a.l; nNode.r = b.r; return nNode; } static Node null() { return {0, 0, (int)-1e18}; } } f[N * 8]; void build(int t, int l, int r) { if (l > r) { return; } f[t].l = l; f[t].r = r; if (l == r) { f[t].max = -INF; return; }
int mid = (l + r) / 2; build(t * 2, l, mid); build(t * 2 + 1, mid + 1, r); pushup(t); } void pushup(int t) { int l = f[t].l, r = f[t].r; f[t] = f[t * 2] + f[t * 2 + 1]; f[t].l = l; f[t].r = r; } Node query(int t, int x, int y) { if (x > y) { return Node::null(); } if (x <= f[t].l && f[t].r <= y) { return f[t]; } if (x > f[t].r || y < f[t].l) { return Node::null(); } return query(t * 2, x, y) + query(t * 2 + 1, x, y); } void Modify(int t, int pos, int v) { if (f[t].l == pos && f[t].r == pos) { f[t].max = max(f[t].max, v); return; } if (f[t].l > pos || f[t].r < pos) { return; } Modify(t * 2, pos, v); Modify(t * 2 + 1, pos, v); pushup(t); }} sgTree;
void solve() { cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; s[i] = s[i - 1] + a[i]; f[i] = -INF; } sgTree.build(1, 1, n * 2);
f[1] = 0; sgTree.Modify(1, 1 + a[1], f[1] - s[1]); int maxidx = 1 + a[1];
for (int i = 2; i <= n; i++) { int qry = sgTree.query(1, i, maxidx).max; if (qry == -INF) { continue; } f[i] = qry + s[i - 1]; sgTree.Modify(1, i + a[i], f[i] - s[i]); maxidx = max(maxidx, i + a[i]); } int ans = a[1] + f[1]; for (int i = 2; i <= n; i++) { ans = max(ans, a[i] + f[i]); } cout << ans << endl;}
signed main() { int T; cin >> T; while (T--) solve(); return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


