【codevs2293】山海经 线段树

题目描述 Description

【Shadow 1】第三题
“南山经之首曰鹊山。其首曰招摇之山,临于西海之上。多桂多金玉。有草焉,其状如韭而青华,其名曰祝馀,食之不饥……
又东三百里曰堂庭之山。金棪木,多白猿,多水玉,多黄金。
又东三百八十里曰猨翼之山。其中多怪兽,水多怪鱼。多白玉,多蝮虫,多怪蛇,多怪木,不可以上……”
《山海经》是以山为纲,以海为线记载古代的河流、植物、动物以及矿产等情况,而且每一条记录路线都不会有重复的山出现。某天,wyl8899想游览《山海经》中的路线,为了简化问题,wyl8899已经把每座山用一个整数表示他对该山的喜恶程度。wyl8899想知道第a座山到第b座山的中间某短路[i,j]能使他感到最满意,即[i,j]这条路上所有山的喜恶程度之和是所有c,d最大。由于wyl8899还要去WJMZBMR家里【哗……】,所以他把这个问题扔给了Shadow:“你做不出来就……”

输入描述 Input Description

第1行,2个整数N,M,N表示一共有N座山,M表示wyl8899想问的次数(Shadow:问那么多干嘛……);
第2行,N个整数,代表N座山的喜恶程度,绝对值均小于10000;
第3-(M+2)行,每行有2个数a,b,表示询问从第a座山到第b座山的最大值。
输出描述 Output Description
一共有M行,每行有3个数i,j,s,表示从第i座山到第j座山总的喜恶程度为s。显然,对于每个询问,有a≤i≤j≤b,如果有多组解,则输出i最少的,如果i也相等,则输出j最少的解。

样例输入 Sample Input

5 3
5 -6 3 -1 4
1 3
1 5
5 5

样例输出 Sample Output

1 1 5
3 5 6
5 5 4

数据范围及提示 Data Size & Hint

对于100%的数据,2≤N≤200000,1≤M≤100000,1≤a≤b≤N。


填了个史前巨坑

维护个区间最大子段和,要维护左右端点…好吧左右端点好恶心

然后机智的我重载加号和小于号…然后交上去还是WA…我以为我不知道哪写错了…然后开始各种改,借鉴黄学长博客什么的…

调了一个小时发现值没求错!端点不是最优!!然后开始输出重载加号!后来发现加号没错然后去看小于号,之后才发现是重载小于号出毛病了…改了个大于号小于号成功AC…我这智商也是没谁了…

好像我没写过线段树维护区间最大子段和…我写的是splay…

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

typedef long long LL;
const int SZ = 1000010;
const int INF = 1000000010;

struct haha{
    int l,r,x;
    void print()
    {
        printf("%d %d %d\n",l,r,x);
    }
};

bool operator <(const haha &a,const haha &b)
{
    return a.x < b.x || (a.x == b.x && a.l > b.l) || (a.x == b.x && a.l == b.l && a.r > b.r);
}

haha operator +(haha a,haha b)
{
    haha c;
    c.l = min(a.l,b.l); c.r = max(a.r,b.r); c.x = a.x + b.x;
    return c;
}

struct segment{
    int l,r;
    haha sum,lx,rx,mx;
}tree[SZ << 2];

void update(int p)
{
    int lch = p << 1,rch = p << 1 | 1;
    tree[p].sum = tree[lch].sum + tree[rch].sum;
    tree[p].lx = max(tree[lch].lx,tree[lch].sum + tree[rch].lx);
    tree[p].rx = max(tree[rch].rx,tree[lch].rx + tree[rch].sum);
    tree[p].mx = max(max(tree[lch].mx,tree[rch].mx),tree[lch].rx + tree[rch].lx);
}

int num[SZ];

void build(int p,int l,int r)
{
    tree[p].l = l;
    tree[p].r = r;
    if(l == r)
    {
        tree[p].sum = tree[p].mx = tree[p].lx = tree[p].rx = (haha){l,r,num[l]};
        return ;
    }
    int mid = l + r >> 1;
    build(p << 1,l,mid);
    build(p << 1 | 1,mid + 1,r);
    update(p);
}


segment ask(int p,int l,int r)
{
    if(l == tree[p].l && tree[p].r == r) 
        return tree[p];
    int mid = tree[p].l + tree[p].r >> 1;
    if(r <= mid) return ask(p << 1,l,r);
    else if(l > mid) return ask(p << 1 | 1,l,r);
    else
    {
        segment a,b,c;
        a = ask(p << 1,l,mid); b = ask(p << 1 | 1,mid + 1,r);
        c.mx = max(max(a.mx,b.mx),a.rx + b.lx);
        c.lx = max(a.lx,a.sum + b.lx);
        c.rx = max(b.rx,a.rx + b.sum);
        c.sum = a.sum + b.sum;
        c.l = l; c.r = r;
        return c;
    }
}


int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&num[i]);
    build(1,1,n);
    for(int i = 1;i <= m;i ++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        segment f = ask(1,l,r);
        printf("%d %d %d\n",f.mx.l,f.mx.r,f.mx.x);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值