题目链接: Potions (Hard Version)

大致题意

有n个数, 编号从1~n, 第i个位置的值为a[i].

从编号为1的数字开始选择, 一直到编号为n的数字. 对于第i个数字, 你可以选或者不选. 若选择的话, 总和会加上a[i].

要求: 你需要选择尽可能多的数字, 并且保证选择每一个数字后, 总和不为负.

解题思路

贪心 + 线段树 (我看大家都是 带反悔的贪心做的, 代码太短了, 让我来个长的)

贪心方向: 我们对于所有正数, 直接取即可. 对于负数的情况, 我们也肯定是优先取大的

基于这个原则, 我们考虑对于负数的情况, 首先我们要从小到大去枚举所有位置的负数.
假设我们此时位于index处, 有一个权值为val的负数. (val当正数去看)

那么此时如果这个val能拿, 我们拿了一定是最优的, 因为我们从小到大枚举了. 那么我们怎么判断这个数字是否能拿呢? 很简单, 我们判断[1, index - 1]的区间和sum 是否 大于等于 val 即可. 若可以拿, 我们直接在[1, index - 1]区间减去val即可.

对于index这个位置, 往后的贡献我们是可以正确计算的, 因为在index以后的位置再求前缀和, 无论如何都会减去val. 考虑到对于index之前的位置: 我们发现其实我们希望尽可能在靠后的位置凑出val.

解释: 假设有序列 3 -3 2 1 -2 1, 那么此时树中的序列应为: 3 0 2 1 0 1
对于-2这个值, 我们希望是由 2 和 1凑出, 即修改后序列变为: 3 0 1 0 0 1

这样我们再枚举到-3这个位置时, 我们可以正确计算出答案.

否则, 如果靠左的去凑数, 即: -2这个值, 我用3来满足他, 树中序列变为: 1 0 2 1 0 1.
我们发现当再枚举到-3时, 此时无法计算出正确答案

我们抱着这个思路, 来分析一下线段树能否满足我们的要求.

首先我们需要求[1, index - 1]的前缀和, 这个OK.

对于之后的修改操作, 我们先要找到尽可能靠右, 能凑出val的位置(假设为pos), 这个也OK.
然后我们需要在这个位置减去相应的贡献, 这个也OK.
再然后呢? 我们其实还要做一件事情, 就是把[pos + 1, index - 1]区间赋为权0. 当然这个还是OK的.

额外提一句, 对于找pos位置的方法:

比较好想的是在树外可以做一个二分, 但这样的方式是O(nlognlogn)

我们其实也可以考虑在树上查询, 就是代码要复杂一些了. 这样可以省去一个logn, 具体见代码

到此为止, 我们快乐的发现, 我们可以写线段树了.

复杂度: O(nlogn)

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define debug(a) cout << #a << " = " << a << endl;
using namespace std;
typedef long long ll;
const int N = 2E5 + 10, INF = 0x3f3f3f3f;
struct node {int l, r;ll val;bool flag; //清空标记
}t[N << 2];
void pushdown(node& op, bool) { op.val = 0, op.flag = 1; }
void pushdown(int x) {if (!t[x].flag) return;pushdown(t[x << 1], 1), pushdown(t[x << 1 | 1], 1);t[x].flag = 0;
}
void pushup(int x) { t[x].val = t[x << 1].val + t[x << 1 | 1].val; }
void build(int l, int r, int x = 1) {t[x] = { l, r, 0, 0 };if (l == r) return;int mid = l + r >> 1;build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}
void modify(int l, int r, int c, int x = 1) { // c若为INF, 则表示置0操作if (l <= t[x].l and r >= t[x].r) {if (c != INF) t[x].val += c;else pushdown(t[x], 1);return;}pushdown(x);int mid = t[x].l + t[x].r >> 1;if (l <= mid) modify(l, r, c, x << 1);if (r > mid) modify(l, r, c, x << 1 | 1);pushup(x);
}
ll ask(int l, int r, int x = 1) { if (l <= t[x].l and r >= t[x].r) return t[x].val;pushdown(x);int mid = t[x].l + t[x].r >> 1;ll res = 0;if (l <= mid) res += ask(l, r, x << 1);if (r > mid) res += ask(l, r, x << 1 | 1);return res;
}int VAL; //我们要凑的值
void fact(int l, int r, int x = 1) {if (!VAL) return;if (t[x].l == t[x].r) { //表示[index, x]区间的和满足要求, 推平[index + 1, x];int index = t[x].l;int can = min(1ll * VAL, t[x].val);t[x].val -= can, VAL -= can;if (!VAL) modify(index + 1, r, INF);return;}pushdown(x);if (l <= t[x].l and r >= t[x].r) { //区间满足要求, 进行树上二分ll right = t[x << 1 | 1].val;if (right >= VAL) fact(l, r, x << 1 | 1); else VAL -= right, fact(l, r, x << 1);pushup(x);return;}int mid = t[x].l + t[x].r >> 1;if (r > mid) fact(l, r, x << 1 | 1); //要先去右区间寻找.if (l <= mid) fact(l, r, x << 1);pushup(x);
}
int main()
{int n; cin >> n;build(1, n);vector<pair<int, int>> v; //存负数的情况int res = 0;rep(i, n) {int x; scanf("%d", &x);if (x >= 0) res++, modify(i, i, x); //正数直接拿else v.push_back({ -x, i });}sort(v.begin(), v.end());for (auto& [val, id] : v) {ll now = ask(1, id); //[1, id] 总贡献有nowif (now >= val) {res++; //表明这个药水一定可以喝VAL = val;fact(1, id);}}cout << res << endl;return 0;
}

END

CF1526C2 Potions (Hard Version) (贪心 + 线段树)相关推荐

  1. [九省联考2018]IIIDX 贪心 线段树

    ~~~题面~~~ 题解: 一开始翻网上题解看了好久都没看懂,感觉很多人都讲得不太详细,所以导致一些细节的地方看不懂,所以这里就写详细一点吧,如果有不对的or不懂的可以发评论在下面. 首先有一个比较明显 ...

  2. CodeForces - 1529E Trees of Tranquillity(贪心+线段树)

    题目链接:https://vjudge.net/problem/CodeForces-1529E 题目大意:给出两棵根节点为 111 的树,分别称为 AAA 树和 BBB 树,现在通过两棵树可以构造出 ...

  3. BZOJ 5249: [2018多省省队联测]IIIDX(贪心 + 线段树)

    题意 这一天,\(\mathrm{Konano}\) 接到了一个任务,他需要给正在制作中的游戏 \(\mathrm{<IIIDX>}\) 安排曲目 的解锁顺序.游戏内共有\(n\) 首曲目 ...

  4. Codeforces Round #620 (Div. 2) F2. Animal Observation (hard version) dp + 线段树

    传送门 文章目录 题意: 思路: 题意: 比如下面这个图: 思路: 对于这个题,比较容易就能考虑到dpdpdp,设f[i][j]f[i][j]f[i][j]为到了第iii行,覆盖了[j,j+k−1][ ...

  5. 【贪心 / 线段树模拟费用流增广】BZOJ4977 [Lydsy八月月赛] 跳伞求生

    [题目] 原题地址 有nn个队友和mm个敌人,每个队友有一个攻击力aia_i,每个敌人有攻击力bib_i和价值cic_i.你可以选择若干个队友,每个队友ii分别去怼一个敌人jj,当ai>bja_ ...

  6. BSOJ4851:攻略 贪心+线段树

    4851 -- [模拟试题]攻略 Description 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景 ...

  7. codeforces:E2. Array and Segments (Hard version)【线段树 + 区间修改】

    分析 思路很简单 遍历每个作为最大值,然后区间不包含当前最大值的都可以减掉 easy version就可以这样暴力解决 然后求出最大差值 暴力解法 import sys input = sys.std ...

  8. 【tyvj】P2065 「Poetize10」封印一击(贪心+线段树/差分)

    http://new.tyvj.cn/p/2065 我就不说我很sb的用线段树来维护值...... 本机自测的时候想了老半天没想出怎么维护点在所有区间被多少区间包含的方法.最后一小时才想出来线段树(果 ...

  9. BZOJ.5249.[九省联考2018]iiidx(贪心 线段树)

    BZOJ LOJ 洛谷 \(d_i\)不同就不用说了,建出树来\(DFS\)一遍. 对于\(d_i\)不同的情况: Solution 1: xxy tql! 考虑如何把这些数依次填到树里. 首先对于已 ...

最新文章

  1. 删除json中的指定元素_DeleteFile,VBA中借助Windows Scripting Host删除指定文件
  2. mysql 2008数据库配置文件_SQL Server 2008数据库的配置及连接
  3. 手机4g模块坏了怎么办_古交联通突然断网,手机没信号,不能接打电话该怎么办?...
  4. 【python】字符串的操作与方法、元组的定义与操作
  5. k8s核心技术-Helm(chart模板的使用下)---K8S_Google工作笔记0049
  6. 分布式一致性算法 之 Paxos算法
  7. 为什么有些人退休后,一下子衰老了很多?
  8. 机器学习课程 Neural Netword for Machine Learning笔记
  9. PDF文档阅读必备的PDF阅读器
  10. 当“雷布斯”遇到“乔布斯”——小米美国专利状况分析
  11. linux增强工具安装过程
  12. 计算机电脑的时区怎么弄,电脑时区怎么设置,教你win10中国时区UTC设置教程
  13. 项目经理领导力提升与塑造:从自己干到团队干
  14. 移动流量转赠给好友_中国移动怎样转赠手机流量?月结流量用不完怎办
  15. 使用思维导图进行产品需求分析
  16. Oracle 按日期分组
  17. 印度人的1919乘法
  18. Flink 相关操作
  19. 智慧树python第四章答案_知到智慧树Python程序设计基础答案章节单元测试答案
  20. 品三国、论国产RISC-V芯片天下大势

热门文章

  1. ServiceMesh实战-服务网格是什么?
  2. Ubuntu18.04 安装搭建 hadoop-3.3.0 集群
  3. 软件测试人员的年终绩效考核怎么应对
  4. 李志敏 华中农业大学计算机学院,华中农业大学计算机教育论坛举行
  5. 核心概念——节点/边/Combo——内置Combo——内置Combo总览
  6. [JavaScript] audio在浏览器中自动播放
  7. HTLM 零基础入门教程(详解)
  8. java解析pdf 图片文字_Java 读取PDF中的文本和图片
  9. C语言学习笔记——(三)静态开辟内存和动态开辟内存
  10. c语言————开辟动态内存空间