假期数学论文->其实是题解之山海经(三哼经)
写该题解原因
才不是为了数学论文呢- 鉴于这么一道线段树的经典题目,网上的题解少之又少, 所以只能自己上,来这么一 篇小小的题解, 嘛, 加深对线段树的理解吧
题意#
“南山之首日鹊山。其首日招摇之山,临于西海之上,多桂,多金玉。有草焉,其状如韭而青华,其名日祝余,食之不饥……又东三百里,日堂庭之山,多?木,多白猿,多水玉,多黄金。又东三百八十里,日?翼之山,其中多怪兽,水多怪鱼,多白玉,多蝮虫,多怪蛇,名怪木,不可以上。……”《山海经》是以山为纲,以海为线记载古代的河流、植物、动物及矿产等情况,(
废话啊呸,陶冶文化情操)
而且每一条记录路线都不会有重复的山出现。某天,你的地理老师想重游《山海经》中的路线,为了简化问题,老师已经把每座山用一个整数表示他对该山的喜恶程度,他想知道第a座山到第b座山的中间某段路(i,j)。能使他感到最满意,即(i,j)这条路上所有山的喜恶度之和是(c,d)(a≤c≤d≤b)最大值。于是老师便向你请教,你能帮助他吗?(其实我不想帮他)
值得注意的是,在《山海经》中,第i座山只能到达第i+1座山。
样例输入
输入第1行是两个数,n,m,2≤n≤100000,1≤m≤100000,n表示一共有n座山,m表示老师想查询的数目。
第2行是n个整数,代表n座山的喜恶度,绝对值均小于10000。
以下m行每行有a,b两个数,1≤a≤j≤b≤m,表示第a座山到第b座山。
5 3
5 -6 3 -1 4
1 3
1 5
5 5
样例输出
一共有m行,每行有3个数i,j,s,表示从第i座山到第j座山总的喜恶度为s。显然,对于每个查询,有a≤i≤j≤b,如果有多组解,则输出i最小的,如果i也相等,则输出j最小的解。
1 1 5
3 5 6
5 5 4
题意分析#
- 其实就是维护一个最大连续子段和,但这个题恶心就恶心在它的输出顺序(介个老师莫非有强迫症?)
所以权值相同要求l、r尽量小,所以相同时选左儿子不选右儿子,维护时考虑是否加等号#
思路
从左端点开始有两种情况
1、是左儿子从他的左端点向后延伸的最大子段和
2、是左儿子的区间总和加上右儿子从他的左端点向后延伸的最大子段和
同理从右端点也有两种情况
1、是右儿子从他的右端点向前延伸的最大子段和
2、是右儿子的区间总和加上左儿子从他的右端点向前延伸的最大子段和
中间的画图可知有三种情况
1、是左儿子的中间最大子段和(内部, 不碰端点)
2、是右儿子的中间最大子段和 (内部, 不碰端点)
3、是左儿子从他的右端点向前延伸的最大子段和加上右儿子他的左端点向后延伸的最大子段和(就是给他拼起来)
Code#
first one 介绍变量
点击查看代码
/*
l, r - > 整个区间的左右端点
lcnt, rcnt -> 左子树的最大连续和 和 右子树(区间)的最大连续和
lright, rleft -> 左子树最大连续和的右端点(从左儿子左端点开始)和右子树最大连续和的左端点(从右儿子右端点开始)
lid -> 最大子段和左端点(中间那段)
rid -> 最大子段和右端点(中间那段)
sum -> 结点权值和
dat -> 区间内部最大子段和 ->就是中间的
*/
struct tree{
int l, r;
int lcnt, rcnt;
int lright, rleft;
int sum, dat, lid, rid;
#define lsp (p << 1)
#define rsp (p << 1 | 1)
#define mid ((t[p].l + t[p].r) >> 1)
}t[maxn << 2];
second one 全部代码精华pushup(参考了别人题解也)
点击查看代码
void pushup(tree &a, const tree &b, const tree &c) {//a是爹 , b是左子树, c是右子树, 如果加了引用就得加const -> 递归传参(未知问题)-> 我太废物,不大清楚
a.sum = b.sum + c.sum;//维护结点(区间)值
if(b.sum + c.lcnt > b.lcnt) {//即是思路从左端点开始的2
a.lcnt = b.sum + c.lcnt;
a.lright = c.lright;//a最右
} else {//即是思路从左端点开始的1
a.lcnt = b.lcnt;
a.lright = b.lright;
}
if (c.rcnt > c.sum + b.rcnt) {//即从右开始的1
a.rcnt = c.rcnt;
a.rleft = c.rleft;
} else {//即从右开始的2
a.rcnt = c.sum + b.rcnt;
a.rleft = b.rleft;
}
//下面都是中间的
if(c.dat > max(b.dat, b.rcnt + c.lcnt)) {
a.dat = c.dat;
a.lid = c.lid;
a.rid = c.rid;
}else {
if((b.dat > b.rcnt + c.lcnt) || (b.dat == b.rcnt + c.lcnt && b.lid <= b.rleft)) {//保证输出顺序
a.dat = b.dat;
a.lid = b.lid;
a.rid = b.rid;
}else {
a.dat = b.rcnt + c.lcnt;
a.lid = b.rleft;
a.rid = c.lright;
}
}
}
third one 其他
点击查看代码
inline void build(int p, int l, int r) {
t[p].l = l;
t[p].r = r;
if (l == r) {
cin >> t[p].sum;
t[p].dat = t[p].lcnt = t[p].rcnt = t[p].sum;
t[p].lid = t[p].rid = t[p].lright = t[p].rleft = l;
return;
}
build(lsp, l, mid);
build(rsp, mid + 1, r);
pushup(t[p], t[lsp], t[rsp]);
}
tree query(int p, int L, int R) {
if (L <= t[p].l && t[p].r <= R) return t[p];
tree ans;
if(R <= mid) ans = query(lsp, L, R);
else if(L > mid) ans = query(rsp, L, R);
else pushup(ans, query(lsp, L, R), query(rsp, L, R));
return ans;
}
signed main() {
cin >> n >> m;
build(1, 1, n);
for (int i = 1, x, y; i <= m; i ++) {
cin >> x >> y;
tree ans = query(1, x, y);
cout << ans.lid << " " << ans.rid << " " << ans.dat << endl;;
}
return 0;
}
AC代码无注释
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n, m;
typedef long long LL;
struct tree{
int l, r;
int lcnt, rcnt;
int lright, rleft;
int sum, dat, lid, rid;
#define lsp (p << 1)
#define rsp (p << 1 | 1)
#define mid ((t[p].l + t[p].r) >> 1)
}t[maxn << 2];
inline void pushup(tree &a, const tree &b, const tree &c) {
a.sum = b.sum + c.sum;
if(b.sum + c.lcnt > b.lcnt) {
a.lcnt = b.sum + c.lcnt;
a.lright = c.lright;
} else {
a.lcnt = b.lcnt;
a.lright = b.lright;
}
if (c.rcnt > c.sum + b.rcnt) {
a.rcnt = c.rcnt;
a.rleft = c.rleft;
} else {
a.rcnt = c.sum + b.rcnt;
a.rleft = b.rleft;
}
if(c.dat > max(b.dat, b.rcnt + c.lcnt)) {
a.dat = c.dat;
a.lid = c.lid;
a.rid = c.rid;
}else {
if((b.dat > b.rcnt + c.lcnt) || (b.dat == b.rcnt + c.lcnt && b.lid <= b.rleft)) {
a.dat = b.dat;
a.lid = b.lid;
a.rid = b.rid;
}else {
a.dat = b.rcnt + c.lcnt;
a.lid = b.rleft;
a.rid = c.lright;
}
}
}
inline void build(int p, int l, int r) {
t[p].l = l;
t[p].r = r;
if (l == r) {
cin >> t[p].sum;
t[p].dat = t[p].lcnt = t[p].rcnt = t[p].sum;
t[p].lid = t[p].rid = t[p].lright = t[p].rleft = l;
return;
}
build(lsp, l, mid);
build(rsp, mid + 1, r);
pushup(t[p], t[lsp], t[rsp]);
}
tree query(int p, int L, int R) {
if (L <= t[p].l && t[p].r <= R) return t[p];
tree ans;
if(R <= mid) ans = query(lsp, L, R);
else if(L > mid) ans = query(rsp, L, R);
else pushup(ans, query(lsp, L, R), query(rsp, L, R));
return ans;
}
signed main() {
cin >> n >> m;
build(1, 1, n);
for (int i = 1, x, y; i <= m; i ++) {
cin >> x >> y;
tree ans = query(1, x, y);
cout << ans.lid << " " << ans.rid << " " << ans.dat << endl;;
}
return 0;
}
我再压个行能不能评全网山海经最短啊(doge)
完结撒花
作者: kiritokazuto
出处:https://www.cnblogs.com/kiritokazuto/p/15828867.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现