前几天看mike的ppt发现有线段树的题,就挑了第一道题搞搞吧,然后就gg了,花了三天时间总算搞掉了
先放题:
775. 山海经
★★★☆ 输入文件:hill.in
输出文件:hill.out
简单对比
时间限制:1 s 内存限制:128 MB
【问题描述】
“南山之首日鹊山。其首日招摇之山,临于西海之上,多桂,多金玉。有草焉,其状如韭而青华,其名日祝余,食之不饥……又东三百里,日堂庭之山,多棪木,多白猿,多水玉,多黄金。
又东三百八十里,日猨翼之山,其中多怪兽,水多怪鱼,多白玉,多蝮虫,多怪蛇,名怪木,不可以上。……”
《山海经》是以山为纲,以海为线记载古代的河流、植物、动物及矿产等情况,而且每一条记录路线都不会有重复的山出现。某天,你的地理老师想重游《山海经》中的路线,为了简化问题,老师已经把每座山用一个整数表示他对该山的喜恶程度,他想知道第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座山。
【输出】
一共有m行,每行有3个数i,j,s,表示从第i座山到第j座山总的喜恶度为s。显然,对于每个查询,有a≤i≤j≤b,如果有多组解,则输出i最小的,如果i也相等,则输出j最小的解。
【输入样例】
5 3
5 -6 3 -1 4
1 3
1 5
5 5
【输出样例】
1 1 5
3 5 6
5 5 4
这道题是真的毒瘤啊,简直就是求最大子段和的魔改版,先后问了许多大佬,先膜拜一下,orz,tql
进入正题:
一、本题基础是线段树求最大子段和
那么如何求最大子段和呢?
线段树核心就是push_up向上回溯的过程,主要是对其进行维护
这样我们得出每个节点必要的三个信息:1、从左端点开始的最大前缀和 2、从右端点开始的最大后缀和 3、中间段的最大子段和
维护的办法我们细说一下
每个最大前缀和有两种情况
1、是左儿子的最大前缀和 2、是左儿子的区间总和加上右儿子的最大前缀和
同理最大后缀和也有两种情况
1、是右儿子的最大后缀和 2、是右儿子的区间总和加上左儿子的最大后缀和
那么中间的怎么搞,画图可知有三种情况
1、是左儿子的中间最大子段和 2、是右儿子的中间最大子段和 3、是左儿子的最大后缀和加上右儿子的最大前缀和
经过上面的分析就可以得出来push_up函数了
附上丑丑的代码
#define ls(x) x<<1 #define rs(x) x<<1|1 struct node { int sum,ls,rs,ms;} st[1000010]; void push_up(int p) { st[p].sum=st[ls(p)].sum+st[rs(p)].sum; st[p].ls=max(st[ls(p)].ls,st[ls(p)].sum+st[rs(p)].ls); st[p].rs=max(st[rs(p)].rs,st[rs(p)].sum+st[ls(p)].rs); st[p].ms=max(st[ls(p)].ms,st[rs(p)].ms); st[p].ms=max(st[p].ms,st[ls(p)].rs+st[rs(p)].ls); }
二、本题就毒瘤在这了,他要输出最大子段和的序号,真的恶心
我们就得想法啊,那么每个节点需要维护的信息还要加上最大中间子段和的左端点,右端点
三、然而这还不够他还要按字典序输出,那么还要加上最大前缀和的右节点,最大后缀和的左节点
最后就是线段树基本操作了
贴代码
#include<bits/stdc++.h> #define ls(x) x<<1 #define rs(x) x<<1|1 using namespace std; const int N=200010; int m,n,k,p,a[N]; struct node { int ls,rs,ms;//ls从左节点开始的最大值,rs是右节点,ms总共的 int l,r,s;//l是区间左节点,r是右节点,s是区间和 int ml,mr;//ml是最大值区间左节点,mr是右节点 int ll,rr; } st[N<<2]; void push_up(int p) { st[p].s=st[ls(p)].s+st[rs(p)].s; if (st[ls(p)].ls>=st[rs(p)].ls+st[ls(p)].s) {st[p].ls=st[ls(p)].ls;st[p].rr=st[ls(p)].rr;} else {st[p].ls=st[ls(p)].s+st[rs(p)].ls;st[p].rr=st[rs(p)].rr;} if (st[rs(p)].rs>=st[rs(p)].s+st[ls(p)].rs) {st[p].rs=st[rs(p)].rs;st[p].ll=st[rs(p)].ll;} else {st[p].rs=st[rs(p)].s+st[ls(p)].rs;st[p].ll=st[ls(p)].ll;} if (st[ls(p)].ms>=st[rs(p)].ms) {st[p].ms=st[ls(p)].ms;st[p].ml=st[ls(p)].ml;st[p].mr=st[ls(p)].mr;} else {st[p].ms=st[rs(p)].ms;st[p].ml=st[rs(p)].ml;st[p].mr=st[rs(p)].mr;} if (st[p].ms>st[ls(p)].rs+st[rs(p)].ls) return; if (st[p].ms<st[ls(p)].rs+st[rs(p)].ls) {st[p].ms=st[ls(p)].rs+st[rs(p)].ls;st[p].ml=st[ls(p)].ll;st[p].mr=st[rs(p)].rr;return;} if (st[p].ml<st[ls(p)].ll) return; if (st[p].ml>st[ls(p)].ll) {st[p].ml=st[ls(p)].ll;st[p].mr=st[rs(p)].rr;return;} st[p].mr=min(st[p].mr,st[rs(p)].rr); } void build(int l,int r,int p) { st[p].l=l;st[p].r=r; if (l==r) {st[p].ml=st[p].mr=st[p].ll=st[p].rr=l; st[p].ls=st[p].rs=st[p].s=st[p].ms=a[l]; return;} int mid=(l+r)>>1; build (l,mid,ls(p)); build (mid+1,r,rs(p)); push_up(p); } node query(int nl,int nr,int p) { int l=st[p].l,r=st[p].r; if (nl<=l&&nr>=r) return st[p]; int mid=(l+r)>>1; if (nr<=mid) return query(nl,nr,ls(p)); if (nl>mid) return query(nl,nr,rs(p)); node x,y,z; x=query(nl,nr,ls(p));y=query(nl,nr,rs(p)); if (x.ls>=x.s+y.ls) {z.ls=x.ls;z.rr=x.rr;z.l=x.l;} else {z.ls=x.s+y.ls;z.rr=y.rr;z.l=x.l;} if (y.rs>=x.rs+y.s) {z.rs=y.rs;z.ll=y.ll;z.r=y.r;} else {z.rs=x.rs+y.s;z.ll=x.ll;z.r=y.r;} if (x.ms>=y.ms) {z.ms=x.ms;z.ml=x.ml;z.mr=x.mr;} else {z.ms=y.ms;z.ml=y.ml;z.mr=y.mr;} if (z.ms>x.rs+y.ls) return z; if (z.ms<x.rs+y.ls) {z.ms=x.rs+y.ls;z.ml=x.ll;z.mr=y.rr;return z;} if (z.ml<x.ll) return z; if (z.ml>x.ll) {z.ml=x.ll;z.mr=y.rr;return z;} z.mr=min(z.mr,y.rr); return z; } int main() { freopen("hill.in","r",stdin); freopen("hill.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,n,1); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); node tmp=query(x,y,1); printf("%d %d %d ",tmp.ml,tmp.mr,tmp.ms); } return 0; }
愿hale越来越强