【线段树】【SOJ1136】【cogs775】山海经

775. [SOJ 1136] 山海经

★★★   输入文件:hill.in   输出文件:hill.out   简单对比

时间限制:1 s   内存限制:128 MB

【问题描述】

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

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

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

【输入】

输入格式(输入文件名hill.in)

输入第1行是两个数,n,m,2≤n≤100000,1≤m≤l00000,n表示一共有n座山,m表示老师想查询的数目。

第2行是n个整数,代表n座山的喜恶度,绝对值均小于10000。

以下m行每行有a,b两个数,1≤a≤j≤b≤m,表示第a座山到第b座山。

【输出】

输出格式(输出文件名hill.out)

一共有m行,每行有3个数i,j,s,表示从第i座山到第j座山总的喜恶度为s。显然,对于每个查询,有a≤i≤j≤b,如果有多组解,则输出i最小的,如果i也相等,则输出j最小的解。

【输入输出样例】

输入(hill.in)

5 3

5 -6 3 -1 4

1 3

1 5

5 5

输出(hill.out)

1 1 5

3 5 6

5 5 4

推荐一个网址,讲得很好——>点击打开链接

简单来说,就是对一个区间储存从左边开始的最值lm及其右端点lr,从右边开始的最值rm及其左端点rl,区间的和sum及区间左右端点l,r,还有区间的最值maxn及其左右端点ml,mr。传递的时候注意一下先后顺序,因为需要左端点尽量靠前。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define root 1,1,n
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
using namespace std;
struct point{
	int l,r,sum,lr,rl,lm,rm,maxn,ml,mr;
}tree[800010];
int n,m,a[100010];
inline int in(){
	int x=0; char ch=getchar(); bool f=true;
	while (ch<'0' || ch>'9'){
		if (ch=='-') f=false;
		ch=getchar();
	}
	while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	if (!f) x=-x;
	return x;
}
inline void push_up(int rt,int l,int r){
	//l,r,sum
	tree[rt].l=l,tree[rt].r=r;
	tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
	//lmaxn,lr
	tree[rt].lm=tree[rt<<1].lm,tree[rt].lr=tree[rt<<1].lr;
	if (tree[rt<<1].sum+tree[rt<<1|1].lm>tree[rt].lm)
		tree[rt].lm=tree[rt<<1].sum+tree[rt<<1|1].lm,tree[rt].lr=tree[rt<<1|1].lr;
	//rmax,rl
	tree[rt].rm=tree[rt<<1|1].rm,tree[rt].rl=tree[rt<<1|1].rl;
	if (tree[rt<<1|1].sum+tree[rt<<1].rm>=tree[rt].rm)
		tree[rt].rm=tree[rt<<1|1].sum+tree[rt<<1].rm,tree[rt].rl=tree[rt<<1].rl;
	//maxn
	tree[rt].maxn=tree[rt<<1].maxn;
	tree[rt].ml=tree[rt<<1].ml,tree[rt].mr=tree[rt<<1].mr;
	if (tree[rt<<1].rm+tree[rt<<1|1].lm>tree[rt].maxn){
		tree[rt].maxn=tree[rt<<1].rm+tree[rt<<1|1].lm;
		tree[rt].ml=tree[rt<<1].rl,tree[rt].mr=tree[rt<<1|1].lr;
	}
	if (tree[rt<<1|1].maxn>tree[rt].maxn){
		tree[rt].maxn=tree[rt<<1|1].maxn;
		tree[rt].ml=tree[rt<<1|1].ml,tree[rt].mr=tree[rt<<1|1].mr;
	}
}
inline void build(int rt,int l,int r){
	if (l==r){
		int x=in();
		tree[rt].sum=tree[rt].lm=tree[rt].rm=tree[rt].maxn=x;
		tree[rt].l=tree[rt].r=tree[rt].lr=tree[rt].rl=tree[rt].ml=tree[rt].mr=l;
		return;
	}
	int mid=(l+r)>>1;
	build(lchild); build(rchild);
	push_up(rt,l,r);
}
inline point query(int rt,int l,int r,int ll,int rr){
	if (ll<=l && r<=rr) return (point)(tree[rt]);
	int mid=(l+r)>>1;
	if (rr<=mid) return query(lchild,ll,rr);
	else if(ll>mid) return query(rchild,ll,rr);
	else {
		point s1,s2,s;
		s1=query(lchild,ll,mid),s2=query(rchild,mid+1,rr);
		//l,r,sum
		s.sum=s1.sum+s2.sum; s.l=s1.l,s.r=s2.r;
		//lmax,lr
		s.lm=s1.lm,s.lr=s1.lr;
		if (s1.sum+s2.lm>s.lm)
			s.lm=s1.sum+s2.lm,s.lr=s2.lr;
		//rmax,rl
		s.rm=s2.rm,s.rl=s2.rl;
		if (s2.sum+s1.rm>=s.rm)
			s.rm=s2.sum+s1.rm,s.rl=s1.rl;
		//maxn,ml,mr
		s.maxn=s1.maxn,s.ml=s1.ml,s.mr=s1.mr;
		if (s1.rm+s2.lm>s.maxn)
			s.maxn=s1.rm+s2.lm,s.ml=s1.rl,s.mr=s2.lr;
		if (s2.maxn>s.maxn)
			s.maxn=s2.maxn,s.ml=s2.ml,s.mr=s2.mr;
		return s;
	}
}
int main(){
	freopen("hill.in","r",stdin);
	freopen("hill.out","w",stdout);
	n=in(),m=in();
	build(root);
	while (m--){
		int x=in(),y=in();
		point k=query(root,x,y);
		printf("%d %d %d\n",k.ml,k.mr,k.maxn);
	}
	return 0;
}
时间: 2024-10-29 19:05:35

【线段树】【SOJ1136】【cogs775】山海经的相关文章

cogs775 山海经 线段树

链接:http://cogs.pro/cogs/problem/problem.php?pid=775 题意:维护区间最大值及其最小字典序来源. 细节巨多--多的狗死人了-- 首先我们要建出一棵线段树,这棵线段树要存放以下几个东西:最长区间,起点,终点,最长前缀,前缀终点,最长后缀,后缀起点.(所以维护打了足足六十行--查询打了三十行--) 如何查询呢?最长序列无外乎三种情况: 1.全在左子树中.我们设$len[i]$为$i$节点最长序列,则此时$len[i]=len[i<<1]$: 2.全在

COGS 775. 山海经 【线段树】

775. 山海经 [问题描述] “南山之首日鹊山.其首日招摇之山,临于西海之上,多桂,多金玉.有草焉,其状如韭而青华,其名日祝余,食之不饥……又东三百里,日堂庭之山,多棪木,多白猿,多水玉,多黄金. 又东三百八十里,日猨翼之山,其中多怪兽,水多怪鱼,多白玉,多蝮虫,多怪蛇,名怪木,不可以上.……” <山海经>是以山为纲,以海为线记载古代的河流.植物.动物及矿产等情况,而且每一条记录路线都不会有重复的山出现.某天,你的地理老师想重游<山海经>中的路线,为了简化问题,老师已经把每座山用

山海经 (线段树高阶操作)

前几天看mike的ppt发现有线段树的题,就挑了第一道题搞搞吧,然后就gg了,花了三天时间总算搞掉了 先放题: 775. 山海经 ★★★☆   输入文件:hill.in   输出文件:hill.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] “南山之首日鹊山.其首日招摇之山,临于西海之上,多桂,多金玉.有草焉,其状如韭而青华,其名日祝余,食之不饥……又东三百里,日堂庭之山,多棪木,多白猿,多水玉,多黄金. 又东三百八十里,日猨翼之山,其中多怪兽,水多怪鱼,多白玉,

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1.这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去. 于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间

bzoj1798: [Ahoi2009]Seq 维护序列seq 线段树

题目传送门 这道题就是线段树 先传乘法标记再传加法 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=400010; LL read(){ LL ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

luogu 1712 区间(线段树+尺取法)

题意:给出n个区间,求选择一些区间,使得一个点被覆盖的次数超过m次,最小的花费.花费指的是选择的区间中最大长度减去最小长度. 坐标值这么大,n比较小,显然需要离散化,需要一个技巧,把区间转化为半开半闭区间,然后线段树的每一个节点表示一个半开半闭区间. 接着我们注意到需要求最小的花费,且这个花费只与选择的区间集合中的最大长度和最小长度有关. 这意味着如果最大长度和最小长度一定,我们显然是需要把中间长度的区间尽量的选择进去使答案不会变的更劣. 不妨把区间按长度排序,枚举每个最小长度区间,然后最大区间

【BZOJ】1382: [Baltic2001]Mars Maps (线段树+扫描线)

1382: [Baltic2001]Mars Maps Time Limit: 5 Sec  Memory Limit: 64 MB Description 给出N个矩形,N<=10000.其坐标不超过10^9.求其面积并 Input 先给出一个数字N,代表有N个矩形. 接下来N行,每行四个数,代表矩形的坐标. Output 输出面积并 Sample Input 2 10 10 20 20 15 15 25 30 Sample Output 225 本以为是傻逼题,没想到不容易啊- 线段树+扫描

BZOJ 1012: [JSOI2008]最大数maxnumber(线段树)

012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MB Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插入操作.语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列