10.25算法训练——裸线段树

题目大意:对N(1<=N<=50000)个数进行连续进行M(1<=M<=200000)次询问:问1-N之间任意连续区间最大值和最小值之差。

之前学过线段树,学的是模版题,求解的问题是在一段区间内任意加减,然后再询问任意一段之区间的和。

这次的问题和之前学的模版题相同之处是:查询的是一段连续区间的信息。

不同之处在于:区间求和问题需要在线段树的每个结点记录其左儿子和右儿子所有结点之和。也就是说每个结点的信息是一个数。

所以之后的查询操作,也是不断去访问线段树的结点,将这些结点上的数加起来即可。

这次的问题是需要知道任意区间的最大值和最小值,因此,每个结点上应该存放的信息是:左儿子和右儿子管辖区间所有数的最大值和最小值。

注意:这里不能把结点信息设计为:左儿子max或min和右儿子max或min的差。因为左儿子管辖区间的最大值最小值之差和右儿子管辖区间

最大值最小值差  不一定是整个区间的最大值最小值之差,也就是说,不满足“区间可加性”。

一旦设计好每个结点应该存放的信息(也就是两个数 max和min ),每次查询到根结点时更新当前的最大值最小值,查询完所有结点后max-min就是答案。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int max,min;
};
const int maxl=50000;
node Sum[maxl<<2];
int A[maxl];
int MAX,MIN;

void PushUp(int rt){
     Sum[rt].max=max(Sum[rt<<1].max, Sum[rt<<1|1].max);
      Sum[rt].min=min(Sum[rt<<1].min, Sum[rt<<1|1].min);
 }  

void Build(int l,int r,int rt)
{
    if(l==r) {
        Sum[rt].max=A[l];
        Sum[rt].min=A[l];
        return;
    }
    int m=(l+r)>>1;
    Build(l,m,rt<<1);
    Build(m+1,r,rt<<1|1);
    PushUp(rt);
}

void Query(int L,int R,int l,int r,int rt)
{
    if(L <= l && r <= R){
        MAX=max(MAX, Sum[rt].max);
        MIN=min(MIN, Sum[rt].min);
        return;
    }
    int m=(l+r)>>1;
    if(L <= m) Query(L,R,l,m,rt<<1);
    if(R >  m) Query(L,R,m+1,r,rt<<1|1);
}

int main()
{
    int N,M;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        for(int i=1;i<=N;++i)
            scanf("%d",&A[i]);
        Build(1,N,1);
        for(int i=0;i<M;i++)
        {
            MAX=-1, MIN=1e7;
            int a,b;
            scanf("%d%d",&a,&b);
            Query(a,b,1,N,1);
            cout << MAX-MIN << endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/bxy0516/p/9863676.html

时间: 2024-11-05 23:24:05

10.25算法训练——裸线段树的相关文章

【算法系列学习】线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵

https://vjudge.net/contest/66989#problem/A 单点修改,区间查询 方法一:线段树 http://www.cnblogs.com/kuangbin/archive/2011/08/15/2139834.html 1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<cmath> 6 #

BZOJ1798题解 Seq维护序列题解 双tag裸线段树

BZOJ1798题解 Seq维护序列题解 双tag裸线段树 1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 5549  Solved: 1951[Submit][Status][Discuss] Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)

【bzoj3387-跨栏训练】线段树+dp

我们可以想到一个dp方程:f[i][0]表示当前在i个栅栏的左端点,f[i][1]表示在右端点. 分两种情况: 第一种:假设现在要更新线段gh的左端点g,而它下来的路径被ef挡住了,那么必定是有ef来更新g. 为什么呢?因为其它点走到g必定要下落,比如说d到g,就相当于d到f再到g. 第二种:假设到ab的路径上没有东西挡着,那就可以直接从源点走过去再直接下落. 按照从上到下的顺序插入线段,线段树就是找当前的某个点被哪条id最大(也就是最低的)线段所覆盖. 1 #include<cstdio>

算法导论学习-线段树(2)

线段树(1)http://www.cnblogs.com/fu11211129/p/4230000.html 1. 线段树应用之动态点插与统计: -------------------------------- 线段树(1)中讲的应用是区段的插值与统计,我们在线段树结构体中接入cover之一域,cover等于0表示该节点所代表的区域并没有被完全覆盖,cover大于等于1表示该节点所代表区域已经被完全覆盖,用线段树(1)博客里面的图来说明一下:           如上图所示,我们最后统计的时候是

算法导论学习-线段树(1)

1. 线段树简介: --------------- 线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b].长度为1的线段称为元线段.非元线段都有两个子结点,左结点代表的线段为[a,(a + b) / 2],右结点代表的线段为[((a + b) / 2)+1,b].若非元线段的编号为 i,那么他的左孩子编号为 2*i,右孩子为 2*i+1.下图是一棵典型的线段树.,一般的,我们把根节点的编号设为1,那么下图很显然的,[1,5]的编号为2,[6,10]的编号为3,其他节点也以此类推. 2.

【BZOJ3064】【Tyvj1518】CPU监控 裸线段树

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43271091"); } 题解:显然是裸的线段树,连区间合并都没有,更别提可持久化了... 水得一比,但是也相当恶心.. 维护一下: 目前线段 最大值.覆盖值.增加值. 历史线段 最大值.覆盖值.增加值. 然后覆盖值是赋-inf还是再加个flag记录有

2016 Multi-University Training Contest 10 [HDU 5861] Road (线段树:区间覆盖+单点最大小)

HDU 5861 题意 在n个村庄之间存在n-1段路,令某段路开放一天需要交纳wi的费用,但是每段路只能开放一次,一旦关闭将不再开放.现在给你接下来m天内的计划,在第i天,需要对村庄ai到村庄bi的道路进行开放.在满足m天内花费最小的情况下,求出每天的花销. 分析: 我们可以想到用线段树想到记录每一段路的开始时间与结束时间,开始时间很简单,就是一开始的时间,结束的时间求法可以参考区间覆盖,这是类似的: 然后我们在转化哪一天开哪些,哪一天关哪些,那这天的贡献sum = 开-关 ; 这很关键,我在比

【10.10校内测试】【线段树维护第k小+删除】【lca+主席树维护前驱后驱】

贪心思想.将a排序后,对于每一个a,找到对应的删除m个后最小的b,每次更新答案即可. 如何删除才是合法并且最优的?首先,对于排了序的a,第$i$个那么之前就应该删除前$i-1$个a对应的b.剩下$m-i+1$可以删,那么在剩下的b中查找第$m-i+2$小即可.每次做完就删除当前a对应的b. 注意离散化. 还有数组不要开大了.... #include<bits/stdc++.h> #define LL long long using namespace std; void read(int &a

10.29算法训练——poj1475双重BFS

题目描述就不多说了,网址在这里-->> http://poj.org/problem?id=1475 看到这题没有一点头绪,于是百度找代码观摩,可是就算这样也是对别人的解法半知半解,不能说找到了完全正确的答案,但对结题过程有了大致了解. 总体思路就是在队箱子bfs的同时,也对人进行bfs.  每移动箱子一个,就对人进行bfs. 对人bfs的目的是判断人能不能走到箱子移动前的后一个位置. 灵魂画师上线: 图中的例子就是人无法推导箱子的位置,也就是说这次bfs无效,开始返回上一步的bfs 网上找的