山海经题解

U234960 山海经

题目背景

“南山经之首,曰鹊山。其首曰招摇之山,临于西海之上,多桂,多金玉。
有草焉,其状如韭而青华,其名日祝余,食之不饥……

又东三百里,曰堂庭之山,多棪木,多白猿,多水玉,多黄金。

又东三百八十里,曰猿翼之山,其中多怪兽,水多怪鱼,多白玉,多蝮虫,多怪蛇,名怪木,不可以上。”

题目描述

《山海经》是以山为纲,以海为线记载古代的河流、植物、动物及矿产等情况,而且每一条记录路线都不会有重复的山出现。

某天,你的地理老师想重游《山海经》中的路线,为了简化问题,老师已经把每座山用一个整数表示他对该山的喜恶程度,他想知道第 \(a\) 座山到第 \(b\) 座山的中间哪段路 \((i, j)\) 能使他感到最满意。于是老师便向你请教,你能帮助他吗?

值得注意的是,在《山海经》中,第 \(i\) 座山只能到达第 \(i+1\) 座山。

我们定义一段路的满意程度为这条路上所有山的喜恶度之和。

输入格式

第一行,两个整数:\(n, m\),表示一共有 \(n\) 座山和表示老师想查询的数目。

第二行是 \(n\) 个整数,代表 \(n\) 座山的喜恶度,绝对值均小于 \(10000\)

接下来 \(m\) 行每行有 \(a, b\) 两个数,表示老师要从第 \(a\) 座山到第 \(b\) 座山。

输出格式

一共有 \(m\) 行,每行有三个数 \(i, j, s\),表示从第 \(i\) 座山到第 \(j\) 座山总的喜恶度为 \(s\)

如果有多组解,则输出i最小的,如果i也相等,则输出j最小的解。

说明/提示

【数据范围】

对于 \(100\%\) 的数据,
\(2 \le n \le 10^5, 1 \le a \le b \le m \le 10^5\)

思路

考虑到要求坐标,所以在结构体中添加坐标变量,坐标在pushup、query、biuld中都要考虑赋值。其余与求区间最大子段和一致。

要考虑I,J取最小,所以取等条件要注意。

线段树与区间最大子段和

注意:build中赋初值时坐标赋为L。

AC CODE

#include <bits/stdc++.h>
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
const int maxn=1e5+10;
int n,m,a[maxn];
struct nd
{
	int l,r,sum,max,pre,back;
	int lr,rl,zuo,you;
}v[maxn<<2];
void pushup(int rt)
{
	v[rt].sum=v[lson].sum+v[rson].sum;//总和 
	if(v[lson].pre>=v[lson].sum+v[rson].pre)
	{
		v[rt].pre=v[lson].pre;
		v[rt].lr=v[lson].lr;
	}
	else
	{
		v[rt].pre=v[lson].sum+v[rson].pre;
		v[rt].lr=v[rson].lr;
	}//左前缀,注意取等
	if(v[rson].back>v[rson].sum+v[lson].back)
	{
		v[rt].back=v[rson].back;
		v[rt].rl=v[rson].rl;
	}
	else
	{
		v[rt].back=v[rson].sum+v[lson].back;
		v[rt].rl=v[lson].rl;
	}//右后缀
	if(v[lson].max>=max(v[rson].max,v[lson].back+v[rson].pre))
	{
		v[rt].max=v[lson].max;
		v[rt].zuo=v[lson].zuo;
		v[rt].you=v[lson].you;
	}//注意取等
	else if(v[rson].max>v[lson].back+v[rson].pre)
	{
		v[rt].max=v[rson].max;
		v[rt].zuo=v[rson].zuo;
		v[rt].you=v[rson].you;
	}
	else
	{
		v[rt].max=v[lson].back+v[rson].pre;
		v[rt].zuo=v[lson].rl;
		v[rt].you=v[rson].lr;
	}//总最长
}
void build(int rt,int l,int r)
{
	v[rt].l=l;
	v[rt].r=r;
	if(l==r)
	{
		v[rt].back=v[rt].max=v[rt].pre=v[rt].sum=a[l];
		v[rt].lr=v[rt].rl=v[rt].zuo=v[rt].you=l;
		return;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(rt);
}
nd find(int rt,int l,int r)
{
	if(v[rt].l>=l&&v[rt].r<=r)
	{
		return v[rt];
	} 
	int mid=(v[rt].l+v[rt].r)>>1;
	if(r<=mid) return find(lson,l,r);
	else if(l>mid) return find(rson,l,r);
	else
	{
		nd left=find(lson,l,r);
		nd right=find(rson,l,r);
		nd res;
		res.l=left.l;//左端点 
		res.r=right.r;//右端点 
		res.sum=left.sum+right.sum;//总和
		if(left.pre>=left.sum+right.pre)
		{
			res.pre=left.pre;
			res.lr=left.lr;
		}
		else
		{
			res.pre=left.sum+right.pre;
			res.lr=right.lr;
		}//左前缀,注意取等
		if(right.back>right.sum+left.back)
		{
			res.back=right.back;
			res.rl=right.rl;
		}
		else
		{
			res.back=right.sum+left.back;
			res.rl=left.rl;
		}//右后缀
		if(left.max>=max(right.max,left.back+right.pre))
		{
			res.max=left.max;
			res.zuo=left.zuo;
			res.you=left.you;
		}//注意取等
		else if(right.max>left.back+right.pre)
		{
			res.max=right.max;
			res.zuo=right.zuo;
			res.you=right.you;
		}
		else
		{
			res.max=left.back+right.pre;
			res.zuo=left.rl;
			res.you=right.lr;
		}//总最长
		return res; 
	}
}
int main () 
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
    	int x,y;
    	cin>>x>>y;
    	nd res=find(1,x,y);
    	cout<<res.zuo<<" "<<res.you<<" "<<res.max<<endl;
	}
    return 0;
}


posted @ 2026-07-15 21:26  wang7979  阅读(8)  评论(0)    收藏  举报