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

前几天看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\n",tmp.ml,tmp.mr,tmp.ms);
  }
  return 0;
}

愿hale越来越强

原文地址:https://www.cnblogs.com/Hale522520/p/10348538.html

时间: 2024-08-07 02:46:47

山海经 (线段树高阶操作)的相关文章

线段树区间更新操作及Lazy思想(详解)

此题题意很好懂:  给你N个数,Q个操作,操作有两种,‘Q a b ’是询问a~b这段数的和,‘C a b c’是把a~b这段数都加上c. 需要用到线段树的,update:成段增减,query:区间求和 介绍Lazy思想:lazy-tag思想,记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率. 在此通俗的解释我理解的Lazy意思,比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果

UVa 11992 Fast Matrix Operations(线段树双懒操作,二维转一维)

题意:给个r*c的矩形,三种操作,将一个子矩形权值+v,将一个子矩阵权值赋值为v,查询一个子矩阵sum,max,min.起初矩阵权值为0,保证r<=20,r*c<=1e6,操作次数不超过10000 链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18697 题解:将二维转一维,设当前点为(x,y),则它在线段树上的点为(x-1)*c+y,由于行不超过20行,不妨枚举每一行,去操作.对于一维的线段树,要进行赋值,加法

poj 3667 Hotel (线段树的合并操作)

Hotel The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as t

CodeForces 620E New Year Tree(线段树的骚操作第二弹)

The New Year holidays are over, but Resha doesn't want to throw away the New Year tree. He invited his best friends Kerim and Gural to help him to redecorate the New Year tree. The New Year tree is an undirected tree with n vertices and root in the v

线段树- 算法训练 操作格子

问题描述 有n个格子,从左到右放成一排,编号为1-n. 共有m次操作,有3种操作类型: 1.修改一个格子的权值, 2.求连续一段格子权值和, 3.求连续一段格子的最大值. 对于每个2.3操作输出你所求出的结果. 输入格式 第一行2个整数n,m. 接下来一行n个整数表示n个格子的初始权值. 接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值. 输出格式 有若干行,行数等于p=2

1.1 线段树的基础操作

本篇对应的是luogu的线段树1 概况: 如下图就是一棵线段树,线段树上的每一个点记录的都是一个区间,所以线段树支持对于区间和点的动态操作,可以在线查询和更改区间上的最值,求和等 时间复杂度:O(n) 使用线段树的情况: 满足区间加法:已知左右两子树的全部信息,一定能够推出父节点 线段树维护的内容根据题目的要求而定 线段树的分类: 根据题目中对于查询和修改区间的不同要求,大致将线段树分为三类: problem1:单点修改,单点查询 problem2:区间修改,单点查询 problem3:区间修改

线段树的复杂操作

一,线段树做区间乘法 首先要明白,乘法操作高于加法操作 一般的话会开long long ,要去模 对于一个节点o,我们设区间和为sum[o],加法标记为add[o],乘法标记为mul[o] mul标记的初始值是1,add标记初始值是0 在修改值的时候,add的维护需要累加,mul的维护需要累乘 此时当我们进行区间加的时候,一切照旧 但是当进行区间乘法的时候 儿子节点的add标记分别先乘上父亲节点的乘标记再加上父亲节点的加标记 (因为父亲节点的加标记已经乘上了一些乘标记了,不需要再乘一次) 儿子节

cogs775 山海经 线段树

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

Sequence operation(线段树区间多种操作)

Sequence operation Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7452    Accepted Submission(s): 2220 Problem Description lxhgww got a sequence contains n characters which are all '0's or '1