本文涉及的基础知识点
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
分治法
[JRKSJ R6] Eltaw
题目背景

你在月下独自行走,不禁想起了一道简单题。
(题目背景图片来自 Phigros 曲绘,如有侵权,请告知出题人。)
题目描述
给你 k k k 个长为 n n n 的序列 a 1 ... k , 1 ... n a_{1\dots k,1\dots n} a1...k,1...n,有 q q q 次询问,每次询问给出一个区间 [ l , r ] [l,r] [l,r],要求出 max i = 1 k ∑ j = l r a i , j \displaystyle\max_{i=1}^k\sum_{j=l}^ra_{i,j} i=1maxkj=l∑rai,j,即求出所有序列中区间 [ l , r ] [l,r] [l,r] 的和的最大值。
输入格式
第一行三个整数 n , k , q n,k,q n,k,q。
接下来 k k k 行,每行 n n n 个整数 a i , j a_{i,j} ai,j。
接下来 q q q 行,每行两个整数 l , r l,r l,r 表示一次询问。
输出格式
输出 q q q 行表示每个询问的答案。
样例 #1
样例输入 #1
7 2 3
1 1 4 5 1 4 0
1 9 1 9 8 1 0
6 7
5 7
1 3
样例输出 #1
4
9
11
提示
Idea:cyffff,Solution:cyffff,Code:cyffff,Data:cyffff
Eltaw - Fl00t (Insane14.4)
本题输入输出文件较大,请使用恰当的输入输出方式。
数据规模
本题采用捆绑测试。
| Subtask \text{Subtask} Subtask | n ≤ n\le n≤ | 特殊限制 | Score \text{Score} Score |
|---|---|---|---|
| 1 1 1 | 5 × 10 3 5\times10^3 5×103 | k ≤ 100 k\le 100 k≤100 | 20 20 20 |
| 2 2 2 | 5 × 10 5 5\times10^5 5×105 | 保证 l = 1 l=1 l=1 | 30 30 30 |
| 3 3 3 | 5 × 10 5 5\times10^5 5×105 | 无 | 50 50 50 |
对于 100 % 100\% 100% 的数据, 1 ≤ n , k , q ≤ 5 × 10 5 1\le n,k,q\le5\times 10^5 1≤n,k,q≤5×105, 1 ≤ n × k ≤ 5 × 10 5 1\le n\times k\le 5\times10^5 1≤n×k≤5×105, 1 ≤ l ≤ r ≤ n 1\le l\le r\le n 1≤l≤r≤n, 0 ≤ a i , j ≤ 10 9 0\le a_{i,j}\le 10^9 0≤ai,j≤109。
数据更新记录
upd 2022.10.05 \text{upd 2022.10.05} upd 2022.10.05:更新了两组数据,分别卡掉了两种时间复杂度错误的做法。感谢 @二叉苹果树 指出。
upd 2022.10.08 \text{upd 2022.10.08} upd 2022.10.08:更新了一组数据,卡掉了记忆化不正确的做法。感谢 @SweetOrangeOvO 指出。
如果你能通过现在的所有测试点,说明你的代码复杂度极可能是正确的。如果你仍认为你的复杂度是错误的,请联系出题人。
前缀和
方式一
L--,R--
preSum[i]记录第i个数列的前缀和。每次查询时,枚举preSum[i][R+1]-preSum[i][L]的最大值,单个查询时间复杂度:O(k),总时间复杂度:O(kq)
预处理
ans[L][R]记录 查询(L,R)的结果。
空间复杂度:O(nn), 时间复杂度:O(nnk)
处理方法
n <=1000,采用预备处理 ,时间复杂度大约 1000 nk ,由于L必须小于等于R,故状态数减半。500nk = 2.5e8。 本题时间2s,能过。
n >= 1000,则k <=500,采用方式一 2.5e8。
用C++的代码超时
cpp
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include <bitset>
using namespace std;
template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
in >> pr.first >> pr.second;
return in;
}
template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) ;
return in;
}
template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
return in;
}
template<class T = int>
vector<T> Read() {
int n;
scanf("%d", &n);
vector<T> ret(n);
for(int i=0;i < n ;i++) {
cin >> ret[i];
}
return ret;
}
template<class T = int>
vector<T> Read(int n) {
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
class Solution {
public:
vector<long long> Ans(vector<vector<int>>& mat, vector<pair<int, int>>& que) {
const int K = mat.size(), N = mat[0].size(), Q = que.size();
vector<vector<long long>> preSum(K, vector<long long>(1));
for (int k = 0; k < K; k++) {
for (int n = 0; n < N; n++) {
preSum[k].emplace_back(preSum[k].back() + mat[k][n]);
}
}
vector<long long> ans;
if (N <= 1000) {
vector<vector<long long>> vInit(N, vector<long long >(N));
for (int left = 0; left < N; left++) {
for (int r = left; r < N; r++) {
for (auto ps : preSum) {
vInit[left][r] = max(vInit[left][r], ps[r + 1] - ps[left]);
}
}
}
for (const auto& [left, r] : que) {
ans.emplace_back(vInit[left - 1][r - 1]);
}
return ans;
}
for (auto [left, r] : que) {
long long cur = 0;
for (auto ps : preSum) {
cur = max(cur, ps[r] - ps[left - 1]);
}
ans.emplace_back(cur);
}
return ans;
}
};
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
int k,n,q;
cin >> n >>k >> q;
vector<vector<int>> mat(k, vector<int>(n));
for (int i = 0; i < k; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &mat[i][j]);
}
}
auto que = Read<pair<int,int>>(q);
#ifdef _DEBUG
//printf("N=%d", n);
//Out(mat, "mat=");
//Out(que, ",que=");
#endif
auto res = Solution().Ans(mat,que);
for (const auto& i : res) {
printf("%lld\r\n", i);
}
return 0;
}
将前缀和换长C方式就可以
用 new long long[...]也可以
cpp
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include <bitset>
using namespace std;
template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
in >> pr.first >> pr.second;
return in;
}
template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) ;
return in;
}
template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
return in;
}
template<class T = int>
vector<T> Read() {
int n;
scanf("%d", &n);
vector<T> ret(n);
for(int i=0;i < n ;i++) {
cin >> ret[i];
}
return ret;
}
template<class T = int>
vector<T> Read(int n) {
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
class Solution {
public:
vector<long long> Ans(vector<vector<int>>& mat, vector<pair<int, int>>& que) {
const int K = mat.size(), N = mat[0].size();
long long preSum[(int)1e6 + 10];
for (int k = 0; k < K; k++) {
preSum[(N + 1) * k] = 0;
for (int n = 0; n < N; n++) {
preSum[(N + 1) * k + n + 1] = preSum[(N + 1) * k + n] + mat[k][n];
}
}
vector<long long> ans(que.size());
if (K >= 500) {
vector<vector<long long>> vInit(N, vector<long long >(N));
for (int left = 0; left < N; left++) {
for (int r = left; r < N; r++) {
for (int k = 0; k < K;k++) {
vInit[left][r] = max(vInit[left][r], preSum[(N+1)*k+r+1] - preSum[(N + 1) * k + left]);
}
}
}
int i = 0;
for (const auto& [left, r] : que) {
ans[i++] = vInit[left - 1][r - 1];
}
return ans;
}
int i = 0;
for (const auto& [left, r] : que) {
for (int k = 0; k < K; k++) {
ans[i] =max(ans[i], preSum[(N + 1) * k + r] - preSum[(N + 1) * k + left-1]);
}
i++;
}
return ans;
}
};
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
int k,n,q;
cin >> n >>k >> q;
vector<vector<int>> mat(k, vector<int>(n));
for (int i = 0; i < k; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &mat[i][j]);
}
}
vector<pair<int, int>> que(q);
for (int i = 0; i < q; i++) {
scanf("%d%d",&que[i].first,&que[i].second);
}
#ifdef _DEBUG
//printf("N=%d", n);
//Out(mat, "mat=");
//Out(que, ",que=");
#endif
auto res = Solution().Ans(mat,que);
for (const auto& i : res) {
printf("%lld\r\n", i);
}
return 0;
}
扩展阅读
| 我想对大家说的话 |
|---|
| 工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
| 学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
| 有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
| 闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
| 如果程序是一条龙,那算法就是他的是睛 |
| 失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。