山海经(线段树)题解

这篇具有很好参考价值的文章主要介绍了山海经(线段树)题解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原题链接:COGS 775
题目描述:
“南山之首曰鹊山。其首曰招摇之山,临于西海之上,多桂,多金玉。有草焉,其状如韭而青华,其名曰祝余,食之不饥……又东三百里,曰堂庭之山,多棪木,多白猿,多水玉,多黄金。又东三百八十里,曰猨翼之山,其中多怪兽,水多怪鱼,多白玉,多蝮虫,多怪蛇,名怪木,不可以上。……”(其实就是一堆废话)
《山海经》是以山为纲,以海为线记载古代的河流、植物、动物及矿产等情况,而且每一条记录路线都不会有重复的山出现。某天,你的地理老师想重游《山海经》中的路线,为了简化问题,老师已经把每座山用一个整数表示他对该山的喜恶程度,他想知道第a座山到第b座山的中间某段路(i,j)能使他感到最满意,即(i,j)这条路上所有山的喜恶度之和是(c,d)(a<=c<=d<=b)最大值。于是老师便向你请教,你能帮助他吗?(我不能)
值得注意的是,在《山海经》中,第i座山只能到达第i+1座山。(好奇怪的规定)
输入格式:
输入第1行是两个数n,m,表示一共有n座山,m表示老师想查询的数目。
第2行是n个整数,代表n座山的喜恶度,绝对值均小于10000。
以下m行每行有a,b两个数,1<=a<=b<=m,表示第a座山到第b座山。
输出格式:
一共有m行,每行有3个数i,j,s,表示从第i座山到第j座山总的喜恶度为s。
显然,对于每个查询,有a<=i<=j<=b,如果有多组解,则输出i最小的,如果i也相等,则输出j最小的解。
思路:
首先,这道题在审题方面就是一个大坎。如果我们设一个变量mx[i][j]代表由i到j的所有山喜恶度的和,那么我们要求的ans[c][d]则是在众多mx[i]j中再找出一个最大值。如果我们就这么打,每一次查询的时间复杂度是O(ab),极限数据下单次也会超时,更不用说多组测试数据了。
那么,既然它维护的是区间特殊值,我们就可以想到用线段树求解。我们要维护的是区间上所有子区间和的最大值,所以我们既要维护一个区间的区间和,还要维护答案。我们分析一下,如果把一个区间分为两个子区间,那么最大区间和的位置有几种情况?
山海经(线段树)题解
很明显,只有这三种。对于前两种来说,只需把左右子树的mx值取一个最大,但是第三种就相当难办。思考一下,这种情况就相当于两个区间拼起来,而这两个区间,一个靠左一个靠右。我们如果想让它们拼接起来的区间最优,就要让它们在满足可以拼接的位置条件下尽可能更优。于是,我定义了mx0和mx1,分别代表靠左区间的最优值和靠右区间的最优值。那mx0和mx1如何由它的子区间推知呢?我们先假设它的两个子区间的mx0,mx1都已经确定,那么左区间的mx0绝对是一种情况,还有就是左区间的总和sm加上右区间的mx0。同理,mx1也应考虑右区间mx0与右区间mx1加左区间mx1。由于mx不考虑位置问题,那么mx绝对不小于mx0和mx1。这样就可以完成这道题的主体部分。至于输出路径,只需在每次更新mx时更新一下就好了。
代码:

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int begin,end;//原区间的位置 
	int sm;//区间和 
	int mx,l,r;//总的最大值及其位置 
	int mx0,l0,r0;//靠左的最大值及其位置
	int mx1,l1,r1;//靠右的最大值及其位置
}str[400004];//线段树 
int a,b,c[100001],u,v;
inline int lft(int x)
{
	return x<<1;//找左区间编号 
}
inline int iht(int x)
{
	return x<<1|1;//找右区间编号 
}
void makstr(int x,int m,int n)//建树 
{
	if(m>n)
	{
		return;
	}
	str[x].begin=m;
	str[x].end=n;
	if(m==n)
	{
		str[x].mx=c[m];
		str[x].mx0=c[m];
		str[x].mx1=c[m];
		str[x].sm=c[m];
		str[x].l0=m;
		str[x].l1=m;
		str[x].l=m;
		str[x].r0=n;
		str[x].r1=n;
		str[x].r=n;
		return;//对单点区间的操作
	}
	else
	{
		int h=(m+n)>>1;//下面递归 
		makstr(lft(x),m,h);
		makstr(iht(x),h+1,n);
		str[x].sm=str[lft(x)].sm+str[iht(x)].sm;//求和 
		if(str[lft(x)].mx>=str[iht(x)].mx)//先把两个子区间比较一下 
		{
			str[x].mx=str[lft(x)].mx;
			str[x].l=str[lft(x)].l;
			str[x].r=str[lft(x)].r;
			//mx相等时很明显左区间的左右边界都更小 
		}
		else
		{
			str[x].mx=str[iht(x)].mx;
			str[x].l=str[iht(x)].l;
			str[x].r=str[iht(x)].r;
		}
		str[x].l0=str[x].begin;//固定的边界 
		if(str[lft(x)].mx0>=str[lft(x)].sm+str[iht(x)].mx0)//找它的mx0 
		{
			str[x].mx0=str[lft(x)].mx0;
			str[x].r0=str[lft(x)].r0;
			//mx0相等时很明显不包含右区间的情况右边界更小 
		}
		else
		{
			str[x].mx0=str[lft(x)].sm+str[iht(x)].mx0;
			str[x].r0=str[iht(x)].r0;
		}
		str[x].r1=str[x].end;//固定的边界
		if(str[iht(x)].mx1>str[iht(x)].sm+str[lft(x)].mx1)//找它的mx1
		{
			str[x].mx1=str[iht(x)].mx1;
			str[x].l1=str[iht(x)].l1;
		}
		else
		{
			str[x].mx1=str[iht(x)].sm+str[lft(x)].mx1;
			str[x].l1=str[lft(x)].l1;
			//mx1相等时很明显包含左区间的情况左边界更小
		}
		if(str[x].mx<str[lft(x)].mx1+str[iht(x)].mx0)//找第三种情况 
		{
			str[x].mx=str[lft(x)].mx1+str[iht(x)].mx0;
			str[x].l=str[lft(x)].l1;
			str[x].r=str[iht(x)].r0;
		}
		else
		{
			if(str[x].mx==str[lft(x)].mx1+str[iht(x)].mx0)//判断是否修改位置 
			{
				if(str[x].l>str[lft(x)].l1)
				{
					str[x].l=str[lft(x)].l1;
					str[x].r=str[iht(x)].r0;
				}
				else
				{
					if(str[x].l==str[lft(x)].l1&&str[x].r>str[iht(x)].r0)
					{
						str[x].r=str[iht(x)].r0;
					}
				}
			}
		}
	}
}
node foudstr(int x,int m,int n)//找值 
{
	if(m==str[x].begin&&n==str[x].end)//递归边界 
	{
		return str[x];
	}
	else
	{
		int h=(str[x].begin+str[x].end)>>1;//下面是递归 
		if(n<=h)
		{
			return foudstr(lft(x),m,n);
		}
		if(m>h)
		{
			return foudstr(iht(x),m,n);
		}
		node j=foudstr(lft(x),m,h),k=foudstr(iht(x),h+1,n),o;
		//以下一部分和上面很相似 
		o.begin=m;
		o.end=n;
		if(j.mx>k.mx)
		{
			o.mx=j.mx;
			o.l=j.l;
			o.r=j.r;
		}
		else
		{
			if(j.mx==k.mx)
			{
				o.mx=j.mx;
				o.l=j.l;
				o.r=j.r;
			}
			else
			{
				o.mx=k.mx;
				o.l=k.l;
				o.r=k.r;
			}
		}
		o.l0=o.begin;
		if(j.mx0>j.sm+k.mx0)
		{
			o.mx0=j.mx0;
			o.r0=j.r0;
		}
		else
		{
			if(j.mx0==j.sm+k.mx0)
			{
				o.mx0=j.mx0;
				o.r0=j.r0;
			}
			else
			{
				o.mx0=j.sm+k.mx0;
				o.r0=k.r0;
			}
		}
		o.r1=o.end;
		if(k.mx1>k.sm+j.mx1)
		{
			o.mx1=k.mx1;
			o.l1=k.l1;
		}
		else
		{
			if(k.mx1==k.sm+j.mx1)
			{
				o.mx1=k.sm+j.mx1;
				o.l1=j.l1;
			}
			else
			{
				o.mx1=k.sm+j.mx1;
				o.l1=j.l1;
			}
		}
		if(o.mx<j.mx1+k.mx0)
		{
			o.mx=j.mx1+k.mx0;
			o.l=j.l1;
			o.r=k.r0;
		}
		else
		{
			if(o.mx==j.mx1+k.mx0)
			{
				if(o.l>j.l1)
				{
					o.l=j.l1;
					o.r=k.r0;
				}
				else
				{
					if(o.l==j.l1)
					{
						if(o.r>k.r0)
						{
							o.r=k.r0;
						}
					}
				}
			}
		}
		return o;
	}
}
int main()
{
	scanf("%d%d",&a,&b);
	for(int i=1;i<=a;i++)
	{
		scanf("%d",&c[i]);
	}
	makstr(1,1,a);
	for(int i=1;i<=b;i++)
	{
		scanf("%d%d",&u,&v);
		node nd=foudstr(1,u,v);
		printf("%d %d %d\n",nd.l,nd.r,nd.mx);
	}
	return 0;
}

总结:
这道题当时我打了一天,主要是因为维护的12个变量是真的想不到。针对最大区间和的情况也想了很久。尤其是最后mx0和mx1不能和mx再进行比较。但是,打完了这道题以后再反过来从理论层面分析一下,发现也并不是非常难。思路通了以后,代码复杂度再高也只需Ctrl+V。文章来源地址https://www.toymoban.com/news/detail-837008.html

到了这里,关于山海经(线段树)题解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法题目题单+题解——图论

    本文为自己做的一部分图论题目,作为题单列出,持续更新。 题单由题目链接和题解两部分组成,题解部分提供简洁题意,代码仓库:Kaiser-Yang/OJProblems。 对于同一个一级标题下的题目,题目难度尽可能做到递增。 题目链接:Luogu P3547 [POI2013] CEN-Price List 题解: 题目链接:

    2024年02月19日
    浏览(38)
  • 题解动态规划:蓝桥杯2022国赛B组 题解 A题目

    在这组题(蓝桥杯C/C++ B组 国赛)里面挑了几道喜欢的题目,做了一下,笔记思路如下。( 其实是我觉得能做出的题 ) 题目图片来源于:CSDN 罚时大师月色 请问2022,拆分成10个不同的正整数有多少种不同的分法。 这道题目,拿到手上的时候,第一个想法是暴力,但是,每次

    2023年04月08日
    浏览(95)
  • BUUCTF_Misc题目题解记录

    仅记录解题步骤,方便自己没事儿的时候拿出来强化一下记忆,俗话说好记性不如烂笔头,祝我早日卷赢同事(? 有没有和我一样用M1芯片,装不了工具,用不惯虚拟机,做不动杂项(那就更不要说pwn了)的大冤种?看过来呜呜呜呜呜…… 1.下载的附件是一张图片(.jpg),在

    2024年02月01日
    浏览(74)
  • 二叉树经典题题解(超全题目)(力扣)

        ✨欢迎来到脑子不好的小菜鸟的文章✨       🎈创作不易,麻烦点点赞哦🎈           所属专栏:刷题             我的主页:脑子不好的小菜鸟           文章特点:关键点和步骤讲解放在           代码相应位置 目录 144. 二叉树的前序遍历 145. 二叉树的后序遍

    2024年04月13日
    浏览(38)
  • 某软件的一个模块的需求规格说明书中描述【软件测试题目】

    某软件的一个模块的需求规格说明书中描述 (1)年薪制员工:严重过失,扣年终风险金的4%;过失,扣年终风险金的2% (2)非年薪制员工:严重过失,扣除当月薪资的8%;过失,扣除当月薪资的4% (1)分析原因及结果 原因 c1:年薪制员工 c2:非年薪制员工 c3:过失 c4:严重过失

    2024年02月08日
    浏览(50)
  • 【Safari浏览器H5分享链接的标题,描述,头像的修改】

    在入口文件html中: 以上修改,分享到QQ中头像失效 解决办法:在入口文件html中添加

    2024年02月16日
    浏览(48)
  • 王道机试指南(第二版)——题目OJ链接

    王道机试指南(第二版)——题目OJ链接 方便大家跳转检验,侵删。 题目 地址 例题2.1 abc(清华大学复试上机题) 例题2.2 反序数(清华大学复试上机题) 例题2.3 对称平方数1(清华大学复试上机题) 习题2.1 与7无关的数(北京大学复试上机题) 习题2.2 百鸡问题(北京哈尔

    2024年01月17日
    浏览(47)
  • 2022 第十四届蓝桥杯模拟赛第二期题目题解(比赛时使用方法)

    目录 第一题:最小的2022 第二题:经过天数 第三题:特殊的十六进制数 第四题:矩阵的最小路径 第五题:质数拆分 第六题:拷贝时间 第七题:单词去重 第八题:最短回文串 第九题:多少个X? 第十题:最小交换 问题描述 请找到一个大于 2022 的最小数,这个数转换成二进

    2023年04月11日
    浏览(71)
  • 数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)

    目录 题目描述 输入示例 输出示例 解题思路  解题方法(C语言) 解析 有序的二叉树遍历可以用堆栈以非递归的方式实现。 例如: 假设遍历一个节点数为6的二叉树(节点数据分别为1到6)时, 堆栈操作为:push(1);push(2);push(3);pop();pop();push(4);pop()

    2024年02月07日
    浏览(50)
  • 【LeetCode题目详解】1281题 整数的各位积和之差 面试题 01.01. 判定字符是否唯一 python题解(作业一二)

    问题描述: 1281. 整数的各位积和之差 给你一个整数 n,请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。 示例 1: 输入:n = 234 输出:15 解释: 各位数之积 = 2 * 3 * 4 = 24 各位数之和 = 2 + 3 + 4 = 9 结果 = 24 - 9 = 15 示例 2: 输入:n = 4421 输出:21 解释:

    2024年02月10日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包