[靈魂拷問♂]系列

[靈魂拷問♂]系列

SP1043 GSS1 - Can you answer these queries I

  • 題目:鏈接
  • 大致題意:求區間最大子段和,不帶修改。

  • 題解:

  • 對於每個線段樹節點。維護以下幾個值:

    l, r:左右端點

    sum:區間和

    val:區間最大子段和

    lv:一定包括區間左端點的最大子段和

    rv:一定包括區間右端點的最大子段和

  • lv轉移:lv = max(左兒子的lv, 左兒子的sum + 右兒子的lv)
  • rv轉移:rv = max(右兒子的rv,右兒子的sum + 左兒子的rv)
  • val轉移:val = max(max(左兒子的val, 右兒子的val), 左兒子rv + 右兒子lv)
  • 這幾種轉移很好理解,就是分類討論。
  • 線段樹的分類討論無非就是都在mid左邊的,都在mid右邊的,橫跨mid的。
  • 注意,查詢的時候。我們不能寫成以下版本。

if(l <= mid) res = max(res, ask(p << 1, l, r));
if(r > mid) res = max(res, ask(p << 1 | 1, l, r));
return res;
  • 因爲這樣寫表達的含義就是指答案只來自於左區間和右區間。例如區間和、區間覆蓋。但是當答案有來自於(取出左右區間的信息然後進行操作後得到的新信息)時,就要分三類討論轉移。
  • 所以這題的查詢代碼如下
T ask(int p, int l, int r) //注意這裏的函數返回值是一個結構體,方便操作
{
    if(t[p].l >= l && t[p].r <= r) return t[p];
    int mid = t[p].l + t[p].r >> 1;
    if(mid < l) return ask(p2, l, r); //答案都來自右兒子
    else if(mid >= r) return ask(p1, l, r); //答案都來自左兒子
    else //答案來自左兒子或右兒子或(左右兒子信息融合後的信息)
    {
        T t1 = ask(p1, l, r), t2 = ask(p2, l, r), now;
        now.sum = t1.sum + t2.sum;
        now.lv = max(t1.lv, t1.sum + t2.lv);
        now.rv = max(t2.rv, t2.sum + t1.rv);
        now.l = l, now.r = r;
        now.val = max(max(t1.val, t2.val), t1.rv + t2.lv);
        return now;
    }
}
  • 代碼:
#include <iostream>
#include <cstdio>
#define N 50005
#define p1 (p << 1)
#define p2 (p << 1 | 1)
using namespace std;

struct T {int l, r, val, lv, rv, sum;} t[N * 4];
int n, m;

int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    return x *= f;
}

void up(int p)
{
    t[p].sum = t[p1].sum + t[p2].sum;
    t[p].lv = max(t[p1].lv, t[p1].sum + t[p2].lv);
    t[p].rv = max(t[p2].rv, t[p2].sum + t[p1].rv);
    t[p].val = max(t[p1].rv + t[p2].lv, max(t[p1].val, t[p2].val));
}

void build(int p, int l, int r)
{
    t[p].l = l, t[p].r = r;
    if(l == r)
    {
        int v = read();
        t[p].lv = t[p].rv = v;
        t[p].val = t[p].sum = v;
        return;
    }
    int mid = l + r >> 1;
    build(p1, l, mid), build(p2, mid + 1, r);
    up(p);
}

T ask(int p, int l, int r)
{
    if(t[p].l >= l && t[p].r <= r) return t[p];
    int mid = t[p].l + t[p].r >> 1;
    if(mid < l) return ask(p2, l, r);
    else if(mid >= r) return ask(p1, l, r);
    else
    {
        T t1 = ask(p1, l, r), t2 = ask(p2, l, r), now;
        now.sum = t1.sum + t2.sum;
        now.lv = max(t1.lv, t1.sum + t2.lv);
        now.rv = max(t2.rv, t2.sum + t1.rv);
        now.l = l, now.r = r;
        now.val = max(max(t1.val, t2.val), t1.rv + t2.lv);
        return now;
    }
}

int main()
{
    cin >> n;
    build(1, 1, n);
    cin >> m;
    for(int i = 1; i <= m; i++)
    {
        int l = read(), r = read();
        printf("%d\n", ask(1, l, r).val);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章