【线段树】codevs 3304 水果姐逛水果街

好长时间不更新啦,急的FireStorm都来催稿了……

题目描述

水果姐今天心情不错,来到了水果街。

水果街有n家水果店,呈直线结构,编号为1~n,每家店能买水果也能卖水果,并且同一家店卖与买的价格一样。

学过oi的水果姐迅速发现了一个赚钱的方法:在某家水果店买一个水果,再到另外一家店卖出去,赚差价。

就在水果姐窃喜的时候,cgh突然出现,他为了为难水果姐,给出m个问题,每个问题要求水果姐从第x家店出发到第y家店,途中只能选一家店买一个水果,然后选一家店(可以是同一家店,但不能往回走)卖出去,求每个问题中最多可以赚多少钱。

另一个版本

一个人,在一个序列上走,单向,多次询问输出最大值减最小值……言简意赅

输入

第一行n,表示有n家店

下来n个正整数,表示每家店一个苹果的价格。

下来一个整数m,表示下来有m个询问。

下来有m行,每行两个整数x和y,表示从第x家店出发到第y家店。

输出

对于询问输出结果

样例

10
2 8 15 1 10 5 19 19 3 5
4
6 6
2 8
2 2
6 3

样例

0
18
0
14

范围

NlogN级别

分析

NlogN + 序列 + 多次询问
想到的一般是线段树……
那么问题来了
线段树,维护什么
首先可以确定的是,答案一定是某个最大值减去最小值(保证方向)
那么便用线段树去维护区间的最大值和最小值(ST?)
然后,正着走的ans和反着走的ans
足够了
答案由三部分构成(以从左往右走为例)
左边的ans
右边的ans
左边的max - 右边的minn
所以线段树就能维护了

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 2000000 + 5;
struct dot
{
    int l,r;
    int minn,maxn;
    int ans1,ans2;
}tree[MAXN*4];
int num[MAXN];
void update(int x)
{
    tree[x].maxn = max(tree[x << 1].maxn,tree[x << 1 | 1].maxn);
    tree[x].minn = min(tree[x << 1].minn,tree[x << 1 | 1].minn);
    tree[x].ans1 = max(tree[x << 1 | 1].maxn - tree[x << 1].minn,max(tree[x << 1].ans1,tree[x << 1 | 1].ans1));
    tree[x].ans2 = max(tree[x << 1].maxn - tree[x << 1 | 1].minn,max(tree[x << 1].ans2,tree[x << 1 | 1].ans2));
    return;
    return;
}
void build(int p,int l,int r)
{
    tree[p].l = l;
    tree[p].r = r;
    if(l == r)
    {
        tree[p].maxn = tree[p].minn = num[l];
        tree[p].ans1 = tree[p].ans2 = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(p << 1,l,mid);
    build(p << 1 | 1,mid + 1,r);
    update(p);
    return;
}
int minn(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)return tree[p].minn;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = 123456789;
    if(l <= mid) ans = min(minn(p << 1,l,r),ans);
    if(mid + 1 <= r) ans = min(minn(p << 1 | 1,l,r),ans);
    return ans;
}
int maxn(int p,int l,int r)
{
    if(l <= tree[p].l && r >= tree[p].r)return tree[p].maxn;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = 0;
    if(l <= mid) ans = max(maxn(p << 1,l,r),ans);
    if(mid + 1 <= r) ans = max(maxn(p << 1 | 1,l,r),ans);
    return ans;
}
int z_ans(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)
        return tree[p].ans1;
    int ans = 0;
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid && mid + 1 <= r)
        ans = max(ans,maxn(p << 1 | 1,mid + 1,r) - minn(p << 1,l,mid));
    if(l <= mid)
        ans = max(ans,z_ans(p << 1,l,r));
    if(mid + 1 <= r)
        ans = max(ans,z_ans(p << 1 | 1,l,r));
    return ans;
}
int f_ans(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)
        return tree[p].ans2;
    int ans = 0;
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid && mid + 1 <= r)
        ans = max(ans,maxn(p << 1,l,mid) - minn(p << 1 | 1,mid + 1,r));
    if(l <= mid)
        ans = max(ans,f_ans(p << 1,l,r));
    if(mid + 1 <= r)
        ans = max(ans,f_ans(p << 1 | 1,l,r));
    return ans;
}
int n,m,l,r;
int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&num[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i = 1;i <= m;i ++)
    {
        scanf("%d %d",&l,&r);
        if(l < r)
            printf("%d\n",z_ans(1,l,r));
        else
            printf("%d\n",f_ans(1,r,l));
    }
    return 0;
}

就这样了……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值