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

Posted hale522520

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了山海经 (线段树高阶操作)相关的知识,希望对你有一定的参考价值。

前几天看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越来越强

以上是关于山海经 (线段树高阶操作)的主要内容,如果未能解决你的问题,请参考以下文章

COGS 775. 山海经 线段树

线段树详解

cogs 775山海经 ——Segment Tree

线段树一些基本的操作;

线段树及其基本操作

线段树分治总结