【二分+前缀和+后缀和】codeforces 2026 D. Sums of Segments
题目
https://codeforces.com/problemset/problem/2026/D
题意
第一行输入一个正整数 \(n(1 \leq n \leq 3e5)\),第二行输入 \(n\) 个整数 \(a_1, a_2, ..., a_i, ..., a_n(-10 \leq a_i \leq 10)\),第三行输入一个正整数 \(q(1 \leq q \leq 3e5)\),随后 \(q\) 行,每行输入两个整数 \(l_i, r_i(1 \leq l_i \leq r_i \leq \frac{n(n+1)}{2})\)。
对于数组 \(a\),我们会得到一个数组 \(b = [a_1, a_1 + a_2, ..., a_1 + a_2 + ... + a_n, a_2, a_2 + a_3, ..., a_2 + a_3 + ... + a_n, ..., a_n]\) 共 \(\frac{n(n + 1)}{2}\) 个元素,对于每个询问要求的是 \(\sum_{j = l_i}^{r_i}{b_j}\)。
题解
对于数组 \(a = [a_1, a_2, ..., a_i, ..., a_n]\),由于 \(1 \leq n \leq 3e5\),那么 \(b\) 元素个数会达到 \(1 \leq \frac{n(n+1)}{2} \leq 45000000000\) 的量级,因此不能直接求出 \(b\) 的每一个元素。不妨对数组 \(b\) 分为 \(n\) 组:
第一组
\(a_1, a_1 + a_2, ..., a1_ + a_2 + ... + a_n\)
第二组
\(a_2, a_2 + a_3, ..., a_2 + a_3 + ... + a_n\)
...
第 \(n\) 组
\(a_n\)
易知从第一组到第 \(n\) 组的元素个数分别是 \(n, n - 1, ..., 1\),若只是求出其中一组,时间复杂度为 \(O(n)\) 是可以接受的。
观察可以发现:第二组可以由第一组每个元素都删去一个 \(a_1\) 得到;第三组可以由第二组每个元素都删去一个 \(a_2\) 得到;以此类推。于是可以先计算出\(sum_1(a_1 + a_2 + ... + a_n)\),\(sum_2(a_2 + ... + a_n)\), ..., \(sum_n(a_n)\)。
假设我想计算 \(b\) 第 \(1 ~ x\) 项的和,那么需要计算出第 \(x\) 项位于第几个 \(sum\),计算方式可以使用二分法。不妨视为第 \(i\) 个,然后计算 \(sum_i\) 需要删掉的元素个数 \(cnt\)。此时问题就转化为如何计算出需要删掉的元素的和,或者是如何计算出最后一组不包括被删除元素的元素之和?
不妨假设 \(n = 4,x = 2\),那么此时 \(cnt = 2,sum_1 = a_1 + (a_1 + a_2) + (a_1 + a_2 + a_3) + (a_1 + a_2 + a_3 + a_4),\sum_{j=1}^{2}{b_j} = a_1 + (a_1 + a_2)\),可得 \(\sum_{j=1}^{2}{b_j} = sum_1 - (a_1 + a_2 + a_3) - (a_1 + a_2 + a_3 + a_4)\)。
删除的元素可以视为 \((a_1 + a_2 + a_3) + (a_1 + a_2 + a_3 + a_4) = 2 \times (a_1 + a_2) + a_3 + (a_3 + a_4) = cnt \times (a_1 + a_2) + a_3 + (a_3 + a_4)\)。观察分好组的数据,易知 \(a_3 + (a_3 + a_4)\) 刚好是倒数第 \(cnt\) 组。由归纳法,可以得出结论:
\(sum_i - sum_cnt - cnt \times 剩余的数最大值\) 就是最后一个 \(sum_i\) 剩余的值。至于前面的 \(sum_1~sum_i-1\) 直接使用前缀和维护即可。
参考代码
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
using namespace std;
using ll = long long;
constexpr int N = 3e5 + 7;
int n, q;
ll l, r;
ll a[N];
ll b[N];//第 i 组元素的总和
ll c[N];//维护第 1~i 组的总元素个数,分别是 n, n + (n - 1), n + (n - 1) + (n - 2), ...
ll apre[N];//a的前缀和
ll apst[N];//a的后缀和
ll bpre[N];//b的前缀和
ll calc(ll x) {//计算出从 b1~bx 的和
int loc = lower_bound(c + 1, c + 1 + n, x) - c;//计算出第 x 项所在组 loc
ll res = bpre[loc];
if (c[loc] > x) {
res = bpre[loc - 1];
int deleteNum = c[loc] - x;//需要删掉的数的数量
ll lastNum = apst[loc] - apst[n - deleteNum + 1];//删掉 deleteNum 个数后剩余最大元素
res += b[loc] - b[n - deleteNum + 1] - lastNum * deleteNum;//第 loc 组的和减去 倒数第 deleteNum 组的和并减去 deletNum 个剩余最大元素
}
return res;
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
apre[i] += apre[i - 1] + a[i];//求数组 a 的前缀和
}
for (int i = n; i >= 1; -- i) {
apst[i] = apst[i + 1] + a[i];//求数组 b 的前缀和
b[i] = apre[i] + b[i + 1];//数组 a 的前缀和的前缀和,也就是第 i 组的和
}
c[1] = n;//第一组的元素个数
for (int i = 2; i <= n; ++ i) {
c[i] = c[i - 1] + (n - i + 1);//第一组到第 i 组的总元素个数
b[i] = b[i - 1] - a[i - 1] * (n - i + 2);//第 i 组的元素之和
}
for (int i = 1; i <= n; ++ i) bpre[i] = b[i] + bpre[i - 1];//数组 b 前缀和的前缀和,也就是第一组到第 i 组的总元素之和
cin >> q;
while (q --) {
cin >> l >> r;
cout << calc(r) - calc(l - 1) << '\n';
}
return 0;
}
【二分+前缀和+后缀和】codeforces 2026 D. Sums of Segments的更多相关文章
- Codeforces Round #381 (Div. 2) D. Alyona and a tree 树上二分+前缀和思想
题目链接: http://codeforces.com/contest/740/problem/D D. Alyona and a tree time limit per test2 secondsm ...
- AcWing:139. 回文子串的最大长度(字符串Hash + 前缀和 + 后缀和 + 二分)
如果一个字符串正着读和倒着读是一样的,则称它是回文的. 给定一个长度为N的字符串S,求他的最长回文子串的长度是多少. 输入格式 输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个 ...
- 139. 回文子串的最大长度(回文树/二分,前缀,后缀和,Hash)
题目链接 : https://www.acwing.com/problem/content/141/ #include <bits/stdc++.h> using namespace st ...
- cf1043E. Mysterious Crime(二分 前缀和)
题意 题目链接 Sol 考场上做完前四题的时候大概还剩半个小时吧,那时候已经困的不行了. 看了看E发现好像很可做?? 又仔细看了几眼发现这不是sb题么... 先考虑两个人,假设贡献分别为\((x, y ...
- POJ 3061 (二分+前缀和or尺取法)
题目链接: http://poj.org/problem?id=3061 题目大意:找到最短的序列长度,使得序列元素和大于S. 解题思路: 两种思路. 一种是二分+前缀和.复杂度O(nlogn).有点 ...
- 递归算法(二)——前缀转后缀
源码:pretopost.cpp #include "stdafx.h" #include <stdio.h> #include <stack> /**** ...
- POJ 2752 Seek the Name, Seek the Fame (KMP的next函数,求前缀和后缀的匹配长度)
给一个字符串S,求出所有前缀,使得这个前缀也正好是S的后缀.升序输出所有情况前缀的长度.KMP中的next[i]的意义就是:前面长度为i的子串的前缀和后缀的最大匹配长度.明白了next[i],那么这道 ...
- 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树
另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...
- 关于字符串 “*****AB**C*D*****” 中前缀、后缀和中间 '*' 的处理
一.删除前缀 '*' #include<iostream> #include<cstdio> using namespace std; //主函数 int main() { ] ...
- Poj 3061 Subsequence(二分+前缀和)
Subsequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12333 Accepted: 5178 Descript ...
随机推荐
- c语言 宏的一些深层应用(##,#,宏函数)
"##" 宏拼接 #define CONCATENATE(a, b) a ## b CONCATENATE(student_, 1) // 将a和b拼接起来变成一个新的变量 -&g ...
- HttpURLConnection和HttpClient使用
HttpURLConnection 这是Java的标准类,继承自URLConnection,可用于向指定网站发送GET/POST请求. 方法描述 void setRequestMethod(Strin ...
- Flutter 2.5 更新详解
Flutter 2.5 正式版已于上周正式发布!这是一次重要的版本更新,也是 Flutter 发布历史上各项统计数据排名第二的版本.我们关闭了 4600 个 Issue,合并了 3932 个 PR,它 ...
- Java序列化、反序列化、反序列化漏洞
目录 1 序列化和反序列化 1.1 概念 1.2 序列化可以做什么? 3 实现方式 3.1 Java 原生方式 3.2 第三方方式 4 反序列化漏洞 1 序列化和反序列化 1.1 概念 Java 中序 ...
- [OI] 模拟退火
模拟退火是一种适合求样本点较大的多峰函数极值的方法. 模拟退火有几个参数:初始温度(\(T_{0}\)),终止温度(\(T_{e}\))和降温参数 \(d\),具体地,模拟退火是让每次的当前温度 \( ...
- 使用MessagePipe实现进程间通信
1.MessagePipe介绍 可以用于.NET和Unity上面的高性能的内存/分布式消息传递管道.适用于发布/订阅模式.CQRS的中介模式.Prism中的EventAggregator.IPC(进程 ...
- iOS生成ipa包的时候总是弹窗提示macOS想要使用系统钥匙串
最近新换了一台苹果电脑,性能不错,不过证书和描述文件需要重新配置,遇到了一系列奇怪的问题.在这里整理记录下来,希望能给其他人提供一些帮助.iOS生成ipa包的时候总是弹窗提示[macOS想要使用系统钥 ...
- 激活windows教程
新建bat文件 [批处理文件:后缀是 bat ] 输入代码: slmgr/skms kms.03k.org slmgr/ato 然后以管理员运行 :
- 从浏览器输入url到回车发生了什么
1. 域名解析,即把域名解析成以为唯一的ip ps:ip是每个网站的对应的一个key,域名是为了语义化,方便使用而设计的 : ps:第一次域名解析需要花费较长的时间,所以一般第一次解析就会把DNS解析 ...
- Android复习(四)权限—>定义自定义应用权限
定义自定义应用权限 本文档介绍了应用开发者如何使用 Android 提供的安全功能来定义自己的权限.通过定义自定义权限,应用可以与其他应用共享其资源和功能.如需详细了解权限,请参阅权限概览. 背景 A ...