【线段树】登山climb

  • 题目描述
    有一座延绵不断、跌宕起伏的山,最低处海拔为0米,最高处海拔不超过8848米。从这座山的一端走到另一端的过程中,每走 1 米海拔高度就升高 1 米或者降低 1 米。有 Q 个登山队计划在这座山的不同区段登山,当他们攀到各自区段内的最高峰时,就会插上他们的队旗。请你写一个程序找出他们插旗的高度。

  • 输入
    第一行为 N(N≤10^6 ),表示山两端的跨度。接下来 N+1 行,每行一个非负整数Hi (i=0..N),表示该位置的海拔高度(单位:米),其中 H0=Hn=0。然后是一个正整数Q(Q≤7000),表示登山队的数量。紧跟的Q行,每行两个数Ai和Bi,表示第 i 个登山队攀爬的区段[Ai,Bi],其中0≤Ai≤Bi≤N。

  • 输出
    共Q行,每行一个整数,表示第 i 个登山队攀爬区段中最高点的高度。
  • 样例输入

10
0
1
2
3
2
3
4
3
2
1
0
5
0 10
2 4
3 7
7 9
8 8

  • 样例输出

4
3
4
3
2

这就是个裸的RMQ 用线段树也可以,但是这不是重点。。。
先上代码

1 . 正常线段树

#include <iostream> 
#include <cstdio> 
#include <vector> 
#include <climits> 
#include <cstdlib> 
using namespace std; 
int a[1000000]={0}; 
int n,k; 
struct treenode { 
    int left,middle,right; 
    int maxx,minn; 
    treenode *lchild; 
    treenode *rchild; 
}; 
treenode* build_tree(int l,int r) 
{ 
    treenode *node=new treenode(); 
    int mid=(l+r)>>1; 
    node->middle=mid; 
    node->left=l; 
    node->right=r; 
    node->lchild=NULL; 
    node->rchild=NULL; 
    if(r-l==1) 
    { 
        node->maxx=a[l];node->minn=a[l];return node; 
    } 
    node->lchild=build_tree(l,mid); 
    node->rchild=build_tree(mid,r); 
    node->maxx=max(node->lchild->maxx,node->rchild->maxx); 
    node->minn=min(node->lchild->minn,node->rchild->minn); 
    return node; 
} 
int query(treenode *node,int l,int r) 
{ 
        if((l==node->left)&&(r==node->right)) 
            return node->maxx; 
        if(l>=node->middle) 
            return query(node->rchild,l,r); 
        if(r<=node->middle) 
            return query(node->lchild,l,r); 
        else
            return max(query(node->lchild,l,node->middle),query(node->rchild,node->middle,r)); 
} 
int main() 
{ 

    int n,m; 
    scanf("%d",&n); 
    for(int i=0;i<=n;i++) 
    { 
        scanf("%d",&a[i]); 
    } 
    treenode *root=build_tree(0,n+1); 
    scanf("%d",&m); 
    for(int i=1;i<=m;i++) 
    { 
        int x,y; 
        scanf("%d%d",&x,&y); 
        int temp=query(root,x,y+1); 
        printf("%d\n",temp); 
    } 
    return 0; 
} 

2 . ZKW线段树

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N=100005;
int M;
int T[4*N];
void PushUP(int rt)
{
    T[rt]=max(T[rt<<1],T[rt<<1|1]);
}
void Build(int n)
{
    for(M=1; M<=n+1; M<<=1);
    for(int i=M+1; i<=M+n+1; i++)
        scanf("%d",&T[i]);
    for(int i=M-1; i>0; i--)
        PushUP(i);
}
void Add(int n,int v)
{
    for(T[n+=M]=v,n>>=1; n; n>>=1)
        PushUP(n);
}
int Query(int s,int t)
{
    int ans=0;
    for(s=s+M-1,t=t+M+1; s^t^1; s>>=1,t>>=1)
    {
        if(~s&1) ans=max(T[s^1],ans);
        if(t&1)  ans=max(T[t^1],ans);
    }
    return ans;
}
int main()
{
    freopen("climb.in","r",stdin);
    freopen("climb.out","w",stdout);
    char str[15];
    int n,t,a,b,k=1,m;
        scanf("%d",&n);
        Build(n);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",Query(a+1,b+1));
    }
    return 0;
}

3 . RMQ

#include<cmath>
#include<iostream>
#include<cstdio>
using namespace std;
#define max(a,b) a>b?a:b
#define min(a,b) a>b?b:a
#define MAX 1000001
int a[MAX], dpmax[MAX][20];
void RMQ(int N)
{
    int i, j;
    for(i=1; i<=N; i++)
    {
        dpmax[i][0] = a[i];
    }
    for(j=1; j<=log(N+1.0)/log(2.0); j++)
        for(i=1; i<=N-(1<<j)+1; i++)
            dpmax[i][j] = max(dpmax[i][j-1], dpmax[i+(1<<(j-1))][j-1]);
}

int main()
{
    int N, Q, i, j, k, aa, bb;
    scanf("%d", &N);
    for(i=1; i<=N+1; i++)
    scanf("%d", &a[i]);
    RMQ(N+1);
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d%d", &i, &j);i++,j++;
        k = log(j - i + 1.0) / log(2.0);
        aa = max(dpmax[i][k], dpmax[j-(1<<k)+1][k]);
        printf("%d\n", aa);
    }
    return 0;
}

RMQ超时了两个点,估计是写挂了,代码先挂上,不要粘去水题哦、

重点是什么来着???
哦对,这题普通线段树写最后两个点1.2秒左右,然而我大重口味(ZKW)线段树只用0.1秒。。秒出哦。。。。。代码还够短哈哈

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值