1 条题解
-
0
自动搬运
来自洛谷,原作者为

An_Aholic
Learn not and know not. || 虫豸!老Q....搬运于
2025-08-24 21:23:39,当前版本为作者最后更新于2023-05-08 22:14:34,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P1314 题解
附:建议 blog 食用。
作者默认读题解的同学以认真读题并思考,不认真读题,你是看不懂地题目分析:
解释式子
先来研究这一串式子:
$$y_i=\sum\limits_{j=l_i}^{r_i}[w_j \ge W] \times \sum\limits_{j=l_i}^{r_i}[w_j \ge W]v_j $$的意思是:从下面的几循环到上面的几,每一次循环加上左边的数。比如 , 可以看作是:
for (int i = 1; i <= n; i++) { sum += a[i]; }中括号的含义是如果满足中括号里面的内容,中括号就可以被看作 ,否则为 ,所以 的意思是:如果满足 , , 否则 。
思路大意
这道题可以使用二分答案来解决。我们二分 后,就可以算出每个区间的检验值 ,然后计算出它们的和 ,将它与 比较,更新二分的上下界。
计算区间的检验值可以使用前缀和,即预处理出数组 和 ,表示前缀和中括号 和 ,然后用 得到区间 的中括号前缀和, 得到区间 的价值和。这样,区间的检验值 就可以表示为最上面一大串巴拉巴拉的式子啦~
在二分答案时,需要判断 和 的大小关系来更新二分的上下界,具体方法
可以参考代码实现听我解释下:如果多了,就说明通过线()太低了,就往上调,否则就说明通过线高了,往下调。代码:
#include <bits/stdc++.h> using namespace std; #define ll long long ll n, m, s; ll y, ans; ll w[200010], v[200010]; ll l[200010], r[200010]; ll qzh1[200010], qzh2[200010]; bool check(ll wq) { y = 0; memset(qzh1, 0, sizeof(qzh1)); memset(qzh2, 0, sizeof(qzh2)); // 多测不清空,爆零两行泪 for (int i = 1; i <= n; i++) { if (w[i] > wq) // > 过了检测通过线 qzh1[i] = qzh1[i - 1] + 1, qzh2[i] = qzh2[i - 1] + v[i]; // 前缀和加上 else qzh1[i] = qzh1[i - 1], qzh2[i] = qzh2[i - 1]; // 承继原来的前缀 } for (int i = 1; i <= m; i++) { int rrrr = r[i]; int llll = l[i]; y += (qzh1[rrrr] - qzh1[llll - 1]) * (qzh2[rrrr] - qzh2[llll - 1]); // 直接照着式子,简单分析,就会发现这跟式子几乎一模一样/doge } if (y > s) return 1; // 判断差值大还是小,为了二分的更改做准备 else return 0; } int main() { cin >> n >> m >> s; for (int i = 1; i <= n; i++) cin >> w[i] >> v[i]; for (int i = 1; i <= m; i++) cin >> l[i] >> r[i]; int lll = 1; int rrr = 2000010; ans = s; while (lll <= rrr) { // 二分答案 int mid = lll + (rrr - lll) / 2; // 防爆ll, 原式子:(lll + rrr) / 2 if (check(mid)) lll = mid + 1; else rrr = mid - 1; ans = min(ans, llabs(s - y)); // 为了防止爆int, 所以写llabs } cout << ans; }
- 1
信息
- ID
- 313
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者