线段树-小白逛公园(nkoj1316)

线段树-小白逛公园(nkoj1316)

这个题稍微难一点

题意分析:维护和查询最大区间子序和,区间大小N,维护和查询次数共M

先了解一下最大子序和:给一个序列,找最大的区间和

LeetCode53. 最大子序和

了解一下O(n)的做法

  • 枚举序列元素,一个变量sum记录当前区间的和,一个ans记录最大的和,如果sumans大就更新ans,如果sum小于0就抛弃sum
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int sum = 0, res = nums.at(0);
        for (int i = 0; i < nums.size(); i++) {
            sum += nums.at(i);
            // 更新现有的最大的
            res = max(res, sum);
            // 如果前面和小于零了那就可以抛弃了
            sum = max(sum, 0);
        }
        return res;
    }
};
  • PS:如果需要知道具体区间则需要sumlsumr记录左右端点的变化

本题的差异

复杂度分析:N是 10 6 ,M是 10 5 ,最坏情况 10 11 ,O(n)做法是不行的,需要找一个log(n)的结构:线段树

线段树复习预习

首先考虑节点存的东西:由于需要查询最大区间和,所以需要维护

  • max:最大子区间和
  • maxl:从左端点开始的最大子区间和
  • maxr:从右端点开始的最大子区间和
  • sum:整个区间的和

然后是线段树的操作

  • build:建立整棵树,递归实现,到达递归基的时候把用读入值设置节点信息,返回的时候需要用左右儿子更新本节点信息(update)
  • modify:将第p个位置的值改为s,同样递归实现,找到p的时候用s设置节点信息,返回的时候更新
  • query:查询a,b区间内的最大子区间和,比较复杂,分为四种情况(四种情况的最大值作为解):
    • 递归基:l,r在a,b里面,直接返回本节点的max
    • 左儿子的子区间有候选解(a<=mid):递归查询左子树
    • 右儿子的子区间有候选解(mid+1<=b):递归查询右子树
    • 候选解和左右儿子都有交(a<=mid&&mid+1<=b):从mid向左的最大子区间和+从mid+1向右的最大子区间和作为候选解

本题坑点:输入数据[a,b]竟然有a>b的情况出现,需要处理一下

代码

struct SegNode {
    int maxl, max, maxr, sum;
    void set(int tmp) {
        maxl = tmp;
        max = tmp;
        maxr = tmp;
        sum = tmp;
    }
};

struct SegTree {
    int n;
    SegNode* nodes;
    SegTree(int n) {
        this->n = n;
        nodes = new SegNode[1 << 20];
        build(1, 1, n);
    }
    void update(int i, int l, int r) {
        int lson = i << 1;
        int rson = i << 1 | 1;
        nodes[i].sum = nodes[lson].sum + nodes[rson].sum;
        nodes[i].max = std::max(nodes[lson].max, nodes[rson].max);
        nodes[i].max = std::max(nodes[i].max, nodes[lson].maxr + nodes[rson].maxl);
        nodes[i].maxl = std::max(nodes[lson].maxl, nodes[lson].sum + nodes[rson].maxl);
        nodes[i].maxr = std::max(nodes[rson].maxr, nodes[rson].sum + nodes[lson].maxr);
    }
    void build(int i, int l, int r) {
        // 到底了
        if (l == r) {
            int tmp;
//            scanf("%d", &tmp);
            read_int(tmp);
//            printf("tmp=%d\n", tmp);
            nodes[i].set(tmp);
            return;
        }
        int mid = (l + r) >> 1;
        build(i << 1, l, mid);
        build(i << 1 | 1, mid + 1, r);
        update(i, l, r);
    }
    void modify(int p, int s) {
        modify(1, 1, n, p, s);
    }
    void modify(int i, int l, int r, int p, int s) {
        // l == r == p
        if (l == r) {
            nodes[i].set(s);
            return;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) {
            modify(i << 1, l, mid, p, s);
        } else {
            modify(i << 1 | 1, mid + 1, r, p, s);
        }
        update(i, l, r);
    }
    int query(int a, int b) {
        if (a > b) {
            int tmp = b;
            b = a;
            a = tmp;
        }
        return query(1, 1, n, a, b);
    }
    int queryl(int i, int l, int r, int b) {
        // l,r在l,b里面
        if (r <= b) {
            return nodes[i].maxl;
        }
        int mid = (l + r) >> 1;
        int ans = -INF;
        ans = queryl(i << 1, l, mid, b);
        if (b >= mid + 1) {
            ans = std::max(ans, nodes[i << 1].sum + queryl(i << 1 | 1, mid + 1, r, b));
        }
        return ans;
    }
    int queryr(int i, int l, int r, int a) {
        // l,r在a,r里面
        if (a <= l) {
            return nodes[i].maxr;
        }
        int mid = (l + r) >> 1;
        int ans = -INF;
        ans = queryr(i << 1 | 1, mid + 1, r, a);
        if (a <= mid) {
            ans = std::max(ans, nodes[i << 1 | 1].sum + queryr(i << 1, l, mid, a));
        }
        return ans;
    }
    int query(int i, int l, int r, int a, int b) {
        // l,r在a,b里面
        if (a <= l && r <= b) {
            return nodes[i].max;
        }
        int mid = (l + r) >> 1;
        int ans = -INF;
        // 左儿子有候选解
        if (a <= mid) {
            ans = query(i << 1, l, mid, a, b);
        }
        // 右儿子有候选解
        if (b >= mid + 1) {
            ans = std::max(ans, query(i << 1 | 1, mid + 1, r, a, b));
        }
        // 候选解和左右儿子都有交
        if (a <= mid && b >= mid + 1) {
            ans = std::max(ans, queryr(i << 1, l, mid, a) + queryl(i << 1 | 1, mid + 1, r, b));
        }
        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/xenoncat/article/details/80181502
今日推荐