775.山海经

★★★ 输入文件:hill.in 输出文件:hill.out 简单对比 时间限制:1 s 内存限制:128 MB

【问题描述】

“南山之首日鹊山。其首日招摇之山,临于西海之上,多桂,多金玉。有草焉,其状如韭而青华,其名日祝余,食之不饥……又东三百里,日堂庭之山,多棪木,多白猿,多水玉,多黄金。

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

​ 《山海经》是以山为纲,以海为线记载古代的河流、植物、动物及矿产等情况,而且每一条记录路线都不会有重复的山出现。某天,你的地理老师想重游《山海经》中的路线,为了简化问题,老师已经把每座山用一个整数表示他对该山的喜恶程度,他想知道第a座山到第b座山的中间某段路(i,j)​(i,j)​(i,j)​。能使他感到最满意,即(i,j)​(i,j)​(i,j)​这条路上所有山的喜恶度之和是(c,d)​(c,d)​(c,d)​(a≤c≤d≤b)​(a≤c≤d≤b)​(a≤c≤d≤b)​最大值。于是老师便向你请教,你能帮助他吗?值得注意的是,在《山海经》中,第i​i​i​座山只能到达第i+1​i+1​i+1​座山。

【输入】

输入第1行是两个数,nnn,m" role="presentation" style="position: relative;">mmm,2≤n≤1000002≤n≤1000002≤n≤100000,1≤m≤1000001≤m≤1000001≤m≤100000,nnn表示一共有n" role="presentation" style="position: relative;">nnn座山,mmm表示老师想查询的数目。

第2行是n" role="presentation" style="position: relative;">nnn个整数,代表nnn座山的喜恶度,绝对值均小于10000" role="presentation" style="position: relative;">100001000010000。

以下mmm行每行有a" role="presentation" style="position: relative;">aaa,bbb两个数,1≤a≤j≤b≤m" role="presentation" style="position: relative;">1≤a≤j≤b≤m1≤a≤j≤b≤m1≤a≤j≤b≤m,表示第aaa座山到第b" role="presentation" style="position: relative;">bbb座山。

【输出】

一共有mmm行,每行有3个数i" role="presentation" style="position: relative;">iii,jjj,s" role="presentation" style="position: relative;">sss,表示从第iii座山到第j" role="presentation" style="position: relative;">jjj座山总的喜恶度为sss。显然,对于每个查询,有a≤i≤j≤b" role="presentation" style="position: relative;">a≤i≤j≤ba≤i≤j≤ba≤i≤j≤b,如果有多组解,则输出iii最小的,如果i" role="presentation" style="position: relative;">iii也相等,则输出jjj最小的解。

【输入样例】

5 3
5 -6 3 -1 4
1 3
1 5
5 5

【输出样例】

1 1 5
3 5 6
5 5 4

【题解】

题目大意:给定N" role="presentation" style="position: relative;">NNN个数,求[L,R][L,R][L,R]区间的最大连续子序列的左端点,右端点以及它的和。

首先考虑DP,DP可以快速求[1,R][1,R][1,R]区间的最大连续子序列,但对于[L,R][L,R][L,R]区间的情况就束手无策了。

想到区间问题有很多用线段树求,那么我们就可以用线段树来做这道题。

令f(L,R)f(L,R)f(L,R)为[L,R][L,R][L,R]区间的最大连续子序列的和,那么f(L,R)=max{f(L,mid),f(mid+1,R)}f(L,R)=max{f(L,mid),f(mid+1,R)}f(L,R)=max\{f(L,mid),f(mid+1,R)\}。

码一遍代码,发现连样例都过不去,仔细想,如果f(L,R)=f(i,j)f(L,R)=f(i,j)f(L,R)=f(i,j)且i<mid,j>midi<mid,j>midimid,那么这样没有算到。

那我们再定义SumL(L,R),SumR(L,R)SumL(L,R),SumR(L,R)Sum_L(L,R),Sum_R(L,R),分别为从RRR往前推的最大连续子序列的和,从L" role="presentation" style="position: relative;">LLL往后推的最大连续子序列的和,因为线段树的性质,可得这段子序列的左端点≥L≥L\ge L,右端点≤R≤R\le R,那么f(L,R)=max{SumL(L,mid)+SumR(mid+1,R)},mid=L+R2f(L,R)=max{SumL(L,mid)+SumR(mid+1,R)},mid=L+R2f(L,R)=max\{Sum_L(L,mid)+Sum_R(mid+1,R)\},mid=\frac{L+R}{2}

这样就把所有情况考虑到了。

对于维护SumL(L,R)SumL(L,R)Sum_L(L,R),
易得SumL(L,R)=max{SumL(L,mid),Sum(L,mid)+SumL(mid+1,R)},mid=L+R2SumL(L,R)=max{SumL(L,mid),Sum(L,mid)+SumL(mid+1,R)},mid=L+R2Sum_L(L,R)=max\{Sum_L(L,mid),Sum(L,mid)+Sum_L(mid+1,R)\},mid=\frac{L+R}{2}
其中Sum(L,R)Sum(L,R)Sum(L,R)是LLL到R" role="presentation" style="position: relative;">RRR的子序列和。

SumR(L,R)SumR(L,R)Sum_R(L,R)同理。

又因为需要知道左右端点,再开4个量维护SumL(L,R)SumL(L,R)Sum_L(L,R)这段子序列的左端点,SumR(L,R)SumR(L,R)Sum_R(L,R)这段子序列的右端点,以及f(L,R)f(L,R)f(L,R)的左右端点即可。

那么这道题就完美解决了。

#include<cstdio>
#include<algorithm>
using namespace std;
struct ad{//L,R表示这个节点覆盖的线段的左端点和右端点,但因为是线段树,可以快速得出L和R的值,在此就不定义了 int LSum,RL; //LSum表示从L往后,最大的连续喜恶值之和,RL表示LSum这段连续子序列的右端点 int RSum,LR; //RSum表示从R往前,最大的连续喜恶值之和,LR表示RSum这段连续子序列的左端点int Sum; //Sum为L到R的数字之和 int Max,LMax,RMax; //Max表示L到R中最大的连续喜恶值之和,LMax表示这段连续子序列的左端点,RMax表示这段连续子序列的右端点 inline void newnode(int data,int id){LSum=RSum=Sum=Max=data;RL=LR=LMax=RMax=id;}//给节点赋初值,因为只有l==R时需要赋初值,所以LSum,RSum,Sum,Max都等于A[L],RL,LR,LMax,RMax都等于L
}T[400005];
int N,M,A[100005];
inline int read(){int Res=0,f=1;char ch=getchar();while (ch>'9'||ch<'0') f=(ch=='-')?-f:f,ch=getchar();while (ch>='0'&&ch<='9') Res=Res*10+ch-'0',ch=getchar();return Res*f;
}
inline void updata(ad &P,ad A,ad B){ //更新 P.LSum=A.LSum;P.RL=A.RL;P.RSum=B.RSum;P.LR=B.LR;P.Sum=A.Sum+B.Sum;P.Max=A.Max;P.LMax=A.LMax;P.RMax=A.RMax;if (A.Sum+B.LSum>P.LSum) P.LSum=A.Sum+B.LSum,P.RL=B.RL;if (B.Sum+A.RSum>P.RSum) P.RSum=B.Sum+A.RSum,P.LR=A.LR;if (A.RSum+B.LSum>P.Max){//题目要求有多组解输出i最小的,所以先判这一段P.Max=A.RSum+B.LSum;P.LMax=A.LR;P.RMax=B.RL;}if (B.Max>P.Max){P.Max=B.Max;P.LMax=B.LMax;P.RMax=B.RMax;}
}
inline void build(int p,int L,int R){if (L==R){T[p].newnode(A[L],L);return ;}int mid=(R+L)>>1;if (L<=mid) build(p<<1,L,mid);if (R>mid) build(p<<1|1,mid+1,R);updata(T[p],T[p<<1],T[p<<1|1]);
}
inline ad query(int p,int L,int R,int qL,int qR){ //qL,qR表示询问的L,R if (qL==L&&qR==R) return T[p];if (L==R) return T[p];int mid=(L+R)>>1;if (mid>=qR) return query(p<<1,L,mid,qL,qR); //如果 [L,mid] 区间包含 [qL,qR] 就直接跑下去 if (qL>mid) return query(p<<1|1,mid+1,R,qL,qR); //如果 [mid+1,R] 区间包含 [qL,qR] 就直接跑下去 ad Ans1=query(p<<1,L,mid,qL,mid),Ans2=query(p<<1|1,mid+1,R,mid+1,qR),Ans; //有交错区间就和普通线段树一样,刷最大的 updata(Ans,Ans1,Ans2);return Ans;
}
int main()
{freopen("hill.in","r",stdin);freopen("hill.out","w",stdout);N=read();M=read();for (int i=1;i<=N;i++) A[i]=read();build(1,1,N);for (int i=1;i<=M;i++) {int L=read(),R=read();ad Res=query(1,1,N,L,R);printf("%d %d %d\n",Res.LMax,Res.RMax,Res.Max);}return 0;
}

【线段树】【cogs775】山海经相关推荐

  1. COGS 775. 山海经 【线段树】

    775. 山海经 [问题描述] "南山之首日鹊山.其首日招摇之山,临于西海之上,多桂,多金玉.有草焉,其状如韭而青华,其名日祝余,食之不饥--又东三百里,日堂庭之山,多棪木,多白猿,多水玉, ...

  2. 【codevs2293】山海经 线段树

    题目描述 Description [Shadow 1]第三题 "南山经之首曰鹊山.其首曰招摇之山,临于西海之上.多桂多金玉.有草焉,其状如韭而青华,其名曰祝馀,食之不饥-- 又东三百里曰堂庭 ...

  3. Sicily 1136 山海经 (SOJ 1136) 【Segment Tree 线段树】

    原题地址:点击打开链接 这题花了整整一天来做,错误基本都是TLE,但是做完非常哈皮,因为感觉比较好地运用了线段树这个数据结构.话说这几天广东不是一般热,中午根本睡不着,满身黏糊糊,课室和图书馆倒成了平 ...

  4. 线段树的进阶:多种信息的维护与传递

     山海经 时间限制:1 s   内存限制:128 MB [问题描述] "南山之首曰鹊山.其首曰招摇之山,临于西海之上,多桂,多金玉.有草焉,其状如韭而青华,其名曰祝余,食之不饥--又东三百里 ...

  5. 二逼平衡树——树套树(线段树套Splay平衡树)

    题面 Bzoj3196 解析 线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了 思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay. ...

  6. 线段树——HDU - 1698

    题目含义 就是初始化一堆数为1 可以经过操作把一个区间的数都改变 并求这堆数的总大小 题目分析 有一个 #include<iostream> #include<stdio.h> ...

  7. BZOJ.1558.[JSOI2009]等差数列(线段树 差分)

    BZOJ 洛谷 首先可以把原序列\(A_i\)转化成差分序列\(B_i\)去做. 这样对于区间加一个等差数列\((l,r,a_0,d)\),就可以转化为\(B_{l-1}\)+=\(a_0\),\(B ...

  8. 【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

    不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路 ...

  9. [bzoj1582][Usaco2009 Hol]Holiday Painting 节日画画_线段树

    Holiday Painting 节日画画 bzoj-1582 Usaco-2009 Hol 题目大意:给定两个n*m的01网格图.q次操作,每次将第二个网格图的子矩阵全部变成0或1,问每一次操作后两 ...

最新文章

  1. Array.asList:数组转list时你一定要知道的“陷阱”!
  2. RESTful_基础知识
  3. 浅谈线程池(中):独立线程池的作用及IO线程池
  4. ACM入门之【线段树】
  5. 4.5-4.9 磁盘格式化,磁盘挂载,手动增加swap空间
  6. python的字典与集合
  7. ActiveMQ结合Spring收发消息
  8. 【POJ - 1850】Code (组合数学,字符串另类排序)
  9. vue.js踩坑之组件参数检验与非props特性
  10. 草稿django添加自定义模板代码的用法
  11. adb命令查看手机电量_desired Capabilities和aapt命令查看手机包信息
  12. String 截取字符串#中间的文本
  13. C#中的Attributes的用法
  14. 漫画:“排序算法” 大总结
  15. ADS(Advanced Design system)使用调谐分析(Tuning)进行电路优化
  16. 计算机win7设置用户密码,Win7怎么设置密码 win7设置开机密码教程
  17. 学习STM32的理由
  18. 魔兽RPG仿魔兽世界:基尔加丹的末日V1.0
  19. 记一次safari浏览器正则表达式兼容问题
  20. codeforces beta round 1

热门文章

  1. 【操作系统概念-作业1】Introduction
  2. 7-1 求1到N的和 (5 分)
  3. Request和Response的使用以及html连接java同步到数据的实例
  4. 实验三——密码破解技术
  5. SEO整站优化方案制作
  6. 简练软考知识点整理-项目管理过程与过程组
  7. PHP图片验证码无法显示的解决方案
  8. 伞源科技Pinpoint和sonarQube对比
  9. 如何使用支付宝支付接口
  10. 服务器遇到攻击了,有什么好的安全解决方案