Reinforced Problem
Reinforced Problem
题目描述
原神是一款开放世界的动作角色扮演游戏,玩家一次可以选择四名角色参与战斗,并且在战斗中可以快速切换。角色可以通过各种方式增强他们的力量,例如提高角色的等级,改进角色装备的圣遗物和武器。
zml 就是一名忠实的原神玩家,但是由于 ffg 对原神的强烈抵制,zml 不得不规划一下自己玩原神的时间。
zml 第 天玩原神都会有一个喜悦值 ,与此同时,ffg 也会在这天有一个对原神玩家的愤怒值 ,并且假如 zml 连续几天玩原神这个愤怒值会累加,假如有一天没有玩原神,ffg 就会原谅他,然后愤怒值清空,但是 zml 的喜悦值不会受到影响。
ffg 有个忍耐值 ,只要 ffg 的愤怒值没有超过 ,ffg 就不会爆发,zml 就可以安然无恙的玩原神,否则 ffg 就会把他炖了。
现在基于 zml 每天的喜悦值和 ffg 对原神玩家的愤怒值,请你输出 zml 的最大喜悦值(当然,zml 不想被炖)。
输入描述:
第一行包含一个整数 ,表示测试用例的数量。
每个测试用例的第一行包含一个整数 , 表示天数, 表示 ffg 的最大愤怒值。
第二行包含 个数 ,表示 zml 在第 天的喜悦值 。
第三行包含 个数 ,表示 ffg 在第 天的愤怒值 。
保证所有测试用例的 加起来的和不超过 。
输出描述:
对于每个测试用例,输出一个整数,表示 zml 玩原神的最大喜悦值(不被邪恶的 ffg 炖的前提下)。
示例1
输入
1
4 6
8 4 3 1
5 2 -2 2
输出
12
说明
对于样例 ,zml 会选择第 天,第 天和第 天
- 第 天 ffg 的愤怒值为 , zml 的喜悦值为 。
- 第 天由于 zml 不选择玩原神,所以 ffg 的愤怒值降为 ,zml 的喜悦值仍然为 。
- 第 天 ffg 的愤怒值为 ,zml 的喜悦值为 。
- 第 天 ffg 的愤怒值为 ,zml 的喜悦值为 。
为什么 zml 不选择第 天,第 天和第 天呢?这连续 天的愤怒值之和是 。 这是因为假如选择第 天,第 天和第 天,ffg 在第 天愤怒值为 ,ffg 就会把 zml 炖了,也就是说,假如 ffg 的愤怒值中途大于 ,即使后面降下来了也无济于事。
示例2
输入
5
6 9
2 4 3 1 5 1
9 1 1 1 -4 2
5 16
8 7 8 4 -1
1 6 6 7 9
5 7
1 1 -1 -2 8
7 -5 9 3 1
6 6
5 8 -2 8 5 -4
7 -5 5 1 5 -2
4 15
-3 3 5 8
-3 10 7 -3
输出
14
23
10
21
13
解题思路
本质上就是选出若干个互不相交的区间,使得这些区间的 总和最大。其中对于每个区间 要满足 (否则第 个区间与第 个区间可以合并成一个区间),且每个区间关于 的任意一个前缀的和不可以超过 ,即 。
考虑 dp,定义 表示从前 天选出的所有合法区间中喜悦值的最大值,根据是否以第 天作为区间的右端点进行状态转移。状态转移方程为
其中 , 表示满足以下要求的下标 的集合:
- 。
- 定义 ,则 ,。
显然直接暴力转移的话时间复杂度是 的。如果 满足条件,那么对于每个 对应的 。因此我们可以先通过 RMQ 维护关于 的区间最大值,在枚举 时,如果区间 内的 的最大值不超过 ,那么就可以从 处转移过来,这样时间复杂度就降到了 。
如果要优化到 意味着不能枚举 了。反过来思考,对于每个 如果以 作为区间的左端点,能不能求出右端点最远可以到达哪里?这是可以的,我们可以二分出这个右端点,对于二分点 ,check 的时候只需判断区间 内的 的最大值是否不超过 。不过在代码实现中,二分出来的是不满足条件的最小位置,也就是最远合法右端点的下一个位置(不存在则设为 )。
这样在从小到大枚举 时,开一个 std::multiset
集合维护 关于 的最大值。对于合法右端点小于 的位置 ,需要将 从集合中删掉。设当前集合中的最大值为 ,则 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
LL sa[N], sb[N];
LL g[18][N];
vector<int> p[N];
LL f[N];
LL query(int l, int r) {
int t = __lg(r - l + 1);
return max(g[t][l], g[t][r - (1 << t) + 1]);
}
void solve() {
int n;
LL m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> sa[i];
sa[i] += sa[i - 1];
}
for (int i = 1; i <= n; i++) {
cin >> sb[i];
sb[i] += sb[i - 1];
}
sb[n + 1] = 0x3f3f3f3f3f3f3f3f;
for (int i = 0; 1 << i <= n + 1; i++) {
for (int j = 1; j + (1 << i) - 1 <= n + 1; j++) {
if (!i) g[i][j] = sb[j];
else g[i][j] = max(g[i - 1][j], g[i - 1][j + (1 << i - 1)]);
}
}
for (int i = 1; i <= n + 1; i++) {
p[i].clear();
}
for (int i = 0; i < n; i++) {
int l = i + 1, r = n + 1;
while (l < r) {
int mid = l + r >> 1;
if (query(i + 1, mid) > sb[i] + m) r = mid;
else l = mid + 1;
}
p[l].push_back(i);
}
multiset<LL> st;
for (int i = 1; i <= n; i++) {
st.insert(f[max(0, i - 2)] - sa[i - 1]);
for (auto &x : p[i]) {
st.erase(st.find(f[max(0, x - 1)] - sa[x]));
}
f[i] = f[i - 1];
if (!st.empty()) f[i] = max(f[i], *st.rbegin() + sa[i]);
}
cout << f[n] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
参考资料
题解 | #环形取数#_牛客博客:httpsblog.nowcoder.netn0aad26a6e07e4e49a85c058b878cc03e
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18558422
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-11-20 G. Restore the Permutation
2022-11-20 Number of Beautiful Partitions
2022-11-20 排队