KSGT - 线段树总结

好久好久以前就想对线段树做一个总结了,多久以前呢?在娘胎里的时候吧

经过两个月打渔式的学习,终于结束了线段树的入门工作,解决了我的第一个线段树专题。至此,我从一个懵逼型选手蜕变为乱写型选手。

犹记得在刚刚进队几个月的时,所有的题目对我来说都是瞎搞题,胡乱猜想,假装证明,曾在一次个人赛中利用刚刚学会的贪心思想,解决了一道据说是经典的线段树题目,现在的我已经不记得那个题目了,自然也不知道应该把这个题目归属于哪一种类型的线段树。

很可惜,那时候没有对当时的代码认真整理、分析,否则的话,我应该会对线段树有更深的了解

所以,我是在瞎搞出那个神奇的题目之后,好久也没有再学习线段树,每天写着百行以内的代码自娱自乐

可是,突然有一天,我就被队友指定下来要攻克数据结构这个莽夫

本身呢,我一个短小精悍的弱质女流被发配去写又臭又长的数据结构我是略有微词的,毕竟天仙一样的女子就该不食人间烟火才是。
可是,本着建设国家、和谐社会的想法,我还是下嫁给了这个一言不合就要强上一小时的男子


一开始的时候,我是跟着《挑战程序设计》开始的入门工作,日本人的脑洞是清奇的,代码的格式也与国内大不相同,好在理解上面没有什么困难(甚至还有帮助?),大致过了一遍,就开始了线段树之旅

最开始遇到的题目,是点更新,区间查询的题目,很简单,很直接,又连着完成了几道,这是的我,突然有一种得到武功秘法的豪气,感觉线段树不过如此,已经学通…

这样的自大狂妄,果然很快就被打脸,仅仅是嚣张了1小时,就遇到了区间更新,区间查询的题目,当时的我还很年轻,爆它呗,点动成线嘛!果不其然,在队友的嘲讽下,完成了TLE的打卡

后来的我, 又遇到了区间合并,二分嵌套,扫描线等等复杂的情况,可是我再也没有那个下午的豪情壮志

写的题目越多,便越发觉得自己渺小,离熟练应用线段树还有很长很长的征途

特别是在区间合并方面,毛病甚多,相比较而言,扫描线的题目做起来更加得心应手

在完成最后一道体积交题目的时候,成竹在胸,甚至有手写代码的把握


现在我试着对所写过的几种类型,进行模板整理

1.对点更新,对区间查询

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int maxn = 50000 + 10;
int a[maxn];

struct SegTree
{
    int l, r;
    int sum;
}segTree[maxn << 2];

void build(int k, int l, int r)
{
    segTree[k].l = l, segTree[k].r = r;
    if (l == r)
    {
        segTree[k].sum = a[l]; // 不再向下更新,赋初始值
        return;
    }
    int m = (l + r) >> 1;
    build(k << 1, l, m);
    build(k << 1 | 1, m + 1, r);
    segTree[k].sum = segTree[k << 1].sum + segTree[k << 1 | 1].sum; // build的push_up操作
}

void update(int k, int p, int num) // 完全暴力更新,从最大更新到最小,所以不需要push_up 和 push_down
{
    if (segTree[k].l <= p && segTree[k].r >= p) segTree[k].sum += num;
    else return;
    if (segTree[k].l == segTree[k].r) return;
    update(k << 1, p, num);
    update(k << 1 | 1, p, num);
}

int query(int k, int l, int r)
{
    if (segTree[k].l > r || segTree[k].r < l) return 0;
    else if (segTree[k].l >= l && segTree[k].r <= r) return segTree[k].sum;
    int v1 = query(k << 1, l, r), v2 = query(k << 1 | 1, l, r);
    return v1 + v2;
}

2.对区间更新,对区间查询

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;

ll a[maxn];

struct SegTree
{
    int l, r;
    ll c;
    ll sum;
}segTree[maxn << 2];

void build(int k, int l, int r)
{
    segTree[k].l = l, segTree[k].r = r, segTree[k].c = 0;
    if (l == r)
    {
        segTree[k].sum = a[l];
        return;
    }
    int m = (l + r) >> 1;
    build(k << 1, l, m);
    build(k << 1 | 1, m + 1, r);
    segTree[k].sum = segTree[k << 1].sum + segTree[k << 1 | 1].sum;
}

void update(int k, int l, int r, int c)
{
    if (segTree[k].l > r || segTree[k].r < l) return;
    if (l <= segTree[k].l && segTree[k].r <= r) //完全包含的时候,直接更新lazy变量
        segTree[k].c += c;
    else
    {
        segTree[k].sum += c * (min(segTree[k].r, r) - max(segTree[k].l, l) + 1); // 部分包含,把孩子更新的值赋值进sum
        //int m = (l + r) >> 1;
        update(k << 1, l, r, c);
        update(k << 1 | 1, l, r, c);
    }
}

ll query(int k, int l, int r)
{
    if (segTree[k].l > r || segTree[k].r < l) return 0;
    else if (l <= segTree[k].l && segTree[k].r <= r)
    {
        return segTree[k].c * (segTree[k].r - segTree[k].l + 1)
            + segTree[k].sum;
    }
    else
    {
        ll v1 = query(k << 1, l, r);
        ll v2 = query(k << 1 | 1, l, r);
        return v1 + v2 + segTree[k].c * (min(segTree[k].r, r) - max(segTree[k].l, l) + 1); //要注意不往下更新的lazy值
    }
}

3.区间合并

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>

using namespace std;
const int maxn = 5e4 + 10;
char str[10];
stack<int> S;

struct SegTree
{
    int l, r;
    int data_l, data_r, data_m;

}segTree[maxn << 2];

void build(int k, int l, int r)
{
    segTree[k].l = l, segTree[k].r = r;
    segTree[k].data_l = segTree[k].data_r = segTree[k].data_m = r - l + 1;
    if (l == r) return;
    int m = (l + r) >> 1;
    build(k << 1, l, m);
    build(k << 1 | 1, m + 1, r);
}

void push_up(int k)
{
    //左右max的更新
    segTree[k].data_l = segTree[k << 1].data_l;
    segTree[k].data_r = segTree[k << 1 | 1].data_r;

    //要注意,可能会超出子区间的范围
    if (segTree[k].data_l == segTree[k << 1].r - segTree[k << 1].l + 1)
        segTree[k].data_l += segTree[k << 1 | 1].data_l;
    if (segTree[k].data_r == segTree[k << 1 | 1].r - segTree[k << 1 | 1].l + 1)
        segTree[k].data_r += segTree[k << 1].data_r;

    //max的更新,左右子区间的max,已经中间连接处的max
    segTree[k].data_m = max(segTree[k << 1].data_m, segTree[k << 1 | 1].data_m);
    segTree[k].data_m = max(segTree[k].data_m,
        segTree[k << 1].data_r + segTree[k << 1 | 1].data_l);
}

void update(int val, int x, int k)
{
    if (segTree[k].l == segTree[k].r) //更新操作只有全空/全满两种,因此push_up,push_down也只要考虑这两种
    {
        segTree[k].data_l = segTree[k].data_m = segTree[k].data_r = val;
        return;
    }
    int m = (segTree[k].l + segTree[k].r) >> 1;
    if (x <= m) update(val, x, k << 1);
    else update(val, x, k << 1 | 1);

    push_up(k);
}

int query(int x, int k) // 以前的写法了,这个query应该是借鉴了kuangbin
{
    if (segTree[k].r == segTree[k].l || segTree[k].data_m == 0
        || segTree[k].data_m == segTree[k].r - segTree[k].l + 1)
        return segTree[k].data_m;
    int m = (segTree[k].r + segTree[k].l) >> 1;
    if (x <= m)
    {
        if (x >= segTree[k << 1].r - segTree[k << 1].data_r + 1)
            return query(x, k << 1) + query(m + 1, k << 1 | 1);
        else return query(x, k << 1);
    }
    else
    {
        if (x <= segTree[k << 1 | 1].l + segTree[k << 1 | 1].data_l - 1)
            return query(m, k << 1) + query(x, k << 1 | 1);
        else return query(x, k << 1 | 1);
    }
}

4.扫描线 - 周长

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>

using namespace std;

const int maxn = 5000 + 10;

struct SegTree
{
    int l, r, cnt, sum;
}segTree[maxn << 2];

struct Line
{
    int a, b, h;
    int flag;
    Line(int a = 0, int b = 0, int h = 0, int flag = 0) //下边为1,上边为-1
    {
        this->a = a, this->b = b, this->h = h, this->flag = flag;
    }
    bool operator < (const Line & rhs) const
    {
        return h < rhs.h || h == rhs.h && flag > rhs.flag; // 周长要注意上下边重合是,下边先进
    }
}hori_line[maxn << 1], vert_line[maxn << 1];

int cur1 = 0, cur2 = 0;
int new_ab[maxn << 1];

int depress(Line * line) //离散化压缩
{
    vector<int> pool;
    for (int i = 0; i < cur1; i++)
    {
        pool.push_back(line[i].a);
        pool.push_back(line[i].b);
    }
    sort(pool.begin(), pool.end());
    pool.erase(unique(pool.begin(), pool.end()), pool.end());
    for (int i = 0; i < cur1; i++)
    {
        int cur = find(pool.begin(), pool.end(), line[i].a) - pool.begin();
        new_ab[cur] = line[i].a; line[i].a = cur;
        cur = find(pool.begin(), pool.end(), line[i].b) - pool.begin();
        new_ab[cur] = line[i].b; line[i].b = cur;
    }
    return pool.size() - 1;
}

void build(int k, int l, int r)
{
    segTree[k].l = l, segTree[k].r = r;
    segTree[k].cnt = 0;
    segTree[k].sum = 0;
    if (l + 1 >= r)
    {
        return;
    }
    int m = (l + r) >> 1;
    build(k << 1, l, m);
    build(k << 1 | 1, m, r);
}

void chan_sum(int k)
{
    if (segTree[k].cnt == 0)
        segTree[k].sum = segTree[k << 1].sum + segTree[k << 1 | 1].sum;
    else
        segTree[k].sum = new_ab[segTree[k].r] - new_ab[segTree[k].l];
}

void update(int k, int l, int r, int c)
{
    if (l > segTree[k].r || r < segTree[k].l) return;
    if (l <= segTree[k].l && segTree[k].r <= r)
    {
        segTree[k].cnt += c;
        chan_sum(k);
        return;
    }
    if (segTree[k].l + 1 == segTree[k].r) //叶子结点的chan_sum是需要特殊处理的,因为没有子节点的影响了
    {
        if (segTree[k].cnt == 0)
            segTree[k].sum = 0;
        else segTree[k].sum = new_ab[segTree[k].r] - new_ab[segTree[k].l];
        return;
    }
    update(k << 1, l, r, c);
    update(k << 1 | 1, l, r, c);
    chan_sum(k);
}

int solve(Line * line)
{
    int w = depress(line);
    build(1, 0, w);
    int res = 0, pre = 0;
    for (int i = 0; i < cur1; i++)
    {
        update(1, line[i].a, line[i].b, line[i].flag);
        int tem = segTree[1].sum;
        res += abs(pre - tem); // 变化的差值,即说明其在外边上,影响周长
        pre = tem;
    }
    return res;
}

5.扫描线 - 面积

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstdlib>

using namespace std;
const int maxn = 1000 + 10;

struct SegTree
{
    int l, r, cnt;
    double sum1, sum2;
}segTree[maxn << 3];

struct Line
{
    int l, r;
    double a, b, h;
    int flag;
    Line(double a = 0.0, double b = 0.0, double h = 0.0, int flag = 0)
    {
        this->a = a, this->b = b, this->h = h, this->flag = flag;
    }
    bool operator < (const Line & rhs) const
    {
        return h < rhs.h || h == rhs.h && flag > rhs.flag;
    }
}line[maxn << 1];

int cur1 = 0;
double new_ab[maxn << 1];

int depress()
{
    vector<double> pool;
    for (int i = 0; i < cur1; i++)
    {
        pool.push_back(line[i].a);
        pool.push_back(line[i].b);
    }
    sort(pool.begin(), pool.end());
    pool.erase(unique(pool.begin(), pool.end()), pool.end());
    for (int i = 0; i < cur1; i++)
    {
        line[i].l = find(pool.begin(), pool.end(), line[i].a) - pool.begin();
        new_ab[line[i].l] = line[i].a;
        line[i].r = find(pool.begin(), pool.end(), line[i].b) - pool.begin();
        new_ab[line[i].r] = line[i].b;
    }
    return pool.size() - 1;
}

void build(int k, int l, int r)
{
    segTree[k].l = l, segTree[k].r = r;
    segTree[k].cnt = 0;
    segTree[k].sum1 = 0.0, segTree[k].sum2 = 0.0;
    if (l + 1 >= r) return;
    int m = (l + r) >> 1;
    build(k << 1, l, m);
    build(k << 1 | 1, m, r);
}

void chan_sum(int k) //相交2次的处理技巧
{
    if (segTree[k].cnt >= 2)
        segTree[k].sum1 = segTree[k].sum2 = new_ab[segTree[k].r] - new_ab[segTree[k].l];
    else if (segTree[k].cnt == 1)
    {
        segTree[k].sum1 = new_ab[segTree[k].r] - new_ab[segTree[k].l];
        segTree[k].sum2 = segTree[k << 1].sum1 + segTree[k << 1 | 1].sum1;
    }
    else
    {
        segTree[k].sum1 = segTree[k << 1].sum1 + segTree[k << 1 | 1].sum1;
        segTree[k].sum2 = segTree[k << 1].sum2 + segTree[k << 1 | 1].sum2;
    }
}

void update(int k, int l, int r, int c)
{
    if (l > segTree[k].r || r < segTree[k].l) return;
    if (l <= segTree[k].l && segTree[k].r <= r)
    {
        segTree[k].cnt += c;
        chan_sum(k);
        return;
    }
    if (segTree[k].l + 1 == segTree[k].r)
    {
        if (segTree[k].cnt >= 2)
            segTree[k].sum1 = segTree[k].sum2 = new_ab[segTree[k].r] - new_ab[segTree[k].l];
        else if (segTree[k].cnt == 1)
        {
            segTree[k].sum1 = new_ab[segTree[k].r] - new_ab[segTree[k].l];
            segTree[k].sum2 = 0;
        }
        else
        {
            segTree[k].sum1 = segTree[k].sum2 = 0;
        }
        return;
    }
    update(k << 1, l, r, c);
    update(k << 1 | 1, l, r, c);
    chan_sum(k);
}

double solve()
{
    int w = depress();
    build(1, 0, w);
    double pre_len = 0.0, pre_h = 0.0;
    double res = 0.0;
    for (int i = 0; i < cur1; i++)
    {
        update(1, line[i].l, line[i].r, line[i].flag);
        res += pre_len * (line[i].h - pre_h); //deta_h * pre_len  分块处理的思想
        pre_len = segTree[1].sum2;
        pre_h = line[i].h;
    }
    return res;
}

5.扫描线 - 体积

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 3000 + 10;

struct Line
{
    ll l, r, h;
    ll z1, z2; //记录高度
    ll flag;
    Line(ll l = 0, ll r = 0, ll h = 0, ll z1 = 0, ll z2 = 0, ll flag = 0)
    {
        this->l = l, this->r = r, this->h = h, this->z1 = z1, this->z2 = z2, this->flag = flag;
    }
    bool operator < (const Line & rhs) const
    {
        return h < rhs.h;
    }
}line[maxn];

struct SegTree
{
    ll l, r;
    ll cnt;
    ll sum1, sum2, sum3;
    void init(ll l, ll r)
    {
        this->l = l, this->r = r;
        sum1 = sum2 = sum3 = 0;
        cnt = 0;
    }
}segTree[maxn << 2];
ll new_x[maxn], new_z[maxn];
ll cur1;

void build(int k, ll l, ll r)
{
    segTree[k].init(l, r);
    if (l + 1 >= r) return;
    ll m = (l + r) >> 1;
    build(k << 1, l, m);
    build(k << 1 | 1, m, r);
}

ll depress_x()
{
    vector<ll> pool;
    for (int i = 0; i < cur1; i++)
    {
        pool.push_back(line[i].l);
        pool.push_back(line[i].r);
    }
    sort(pool.begin(), pool.end());
    pool.erase(unique(pool.begin(), pool.end()), pool.end());
    for (int i = 0; i < cur1; i++)
    {
        ll cur;
        cur = find(pool.begin(), pool.end(), line[i].l) - pool.begin();
        new_x[cur] = line[i].l; line[i].l = cur;

        cur = find(pool.begin(), pool.end(), line[i].r) - pool.begin();
        new_x[cur] = line[i].r; line[i].r = cur;
    }
    return pool.size() - 1;
}

ll depress_z()
{
    vector<ll> pool;
    for (int i = 0; i < cur1; i++)
    {
        pool.push_back(line[i].z1);
        pool.push_back(line[i].z2);
    }
    sort(pool.begin(), pool.end());
    pool.erase(unique(pool.begin(), pool.end()), pool.end());
    for (int i = 0; i < cur1; i++)
    {
        ll cur;
        cur = find(pool.begin(), pool.end(), line[i].z1) - pool.begin();
        new_z[cur] = line[i].z1;

        cur = find(pool.begin(), pool.end(), line[i].z2) - pool.begin();
        new_z[cur] = line[i].z2;
    }
    return pool.size() - 1;
}

void chan_sum(int k)
{
    if (segTree[k].cnt >= 3)
    {
        segTree[k].sum3 = new_x[segTree[k].r] - new_x[segTree[k].l];
        segTree[k].sum2 = new_x[segTree[k].r] - new_x[segTree[k].l];
        segTree[k].sum1 = new_x[segTree[k].r] - new_x[segTree[k].l];
    }
    else if (segTree[k].cnt == 2)
    {
        segTree[k].sum3 = segTree[k << 1].sum1 + segTree[k << 1 | 1].sum1;
        segTree[k].sum2 = new_x[segTree[k].r] - new_x[segTree[k].l];
        segTree[k].sum1 = new_x[segTree[k].r] - new_x[segTree[k].l];
    }
    else if (segTree[k].cnt == 1)
    {
        segTree[k].sum3 = segTree[k << 1].sum2 + segTree[k << 1 | 1].sum2;
        segTree[k].sum2 = segTree[k << 1].sum1 + segTree[k << 1 | 1].sum1;
        segTree[k].sum1 = new_x[segTree[k].r] - new_x[segTree[k].l];
    }
    else if (segTree[k].cnt == 0)
    {
        segTree[k].sum3 = segTree[k << 1].sum3 + segTree[k << 1 | 1].sum3;
        segTree[k].sum2 = segTree[k << 1].sum2 + segTree[k << 1 | 1].sum2;
        segTree[k].sum1 = segTree[k << 1].sum1 + segTree[k << 1 | 1].sum1;
    }
}

void update(int k, ll l, ll r, ll c)
{
    if (l > segTree[k].r || r < segTree[k].l) return;
    if (l <= segTree[k].l && segTree[k].r <= r)
    {
        segTree[k].cnt += c;
        chan_sum(k);
        return;
    }
    if (segTree[k].l + 1 == segTree[k].r)
    {
        if (segTree[k].cnt >= 3)
        {
            segTree[k].sum3 = new_x[segTree[k].r] - new_x[segTree[k].l];
            segTree[k].sum2 = new_x[segTree[k].r] - new_x[segTree[k].l];
            segTree[k].sum1 = new_x[segTree[k].r] - new_x[segTree[k].l];
        }
        else if (segTree[k].cnt == 2)
        {
            segTree[k].sum3 = 0;
            segTree[k].sum2 = new_x[segTree[k].r] - new_x[segTree[k].l];
            segTree[k].sum1 = new_x[segTree[k].r] - new_x[segTree[k].l];
        }
        else if (segTree[k].cnt == 1)
        {
            segTree[k].sum3 = 0;
            segTree[k].sum2 = 0;
            segTree[k].sum1 = new_x[segTree[k].r] - new_x[segTree[k].l];
        }
        else if (segTree[k].cnt == 0)
        {
            segTree[k].sum3 = 0;
            segTree[k].sum2 = 0;
            segTree[k].sum1 = 0;
        }
        return;
    }
    update(k << 1, l, r, c);
    update(k << 1 | 1, l, r, c);
    chan_sum(k);
}

ll square(ll down_lim, ll up_lim)
{
    ll pre_h = 0, pre_len = 0;
    ll res = 0;
    for (int i = 0; i < cur1; i++)
    {
        if (line[i].z1 <= down_lim && up_lim <= line[i].z2) //利用记录下的高度,来筛选本次扫描哪些块
        {
            update(1, line[i].l, line[i].r, line[i].flag);
            res += (line[i].h - pre_h) * pre_len;
            pre_h = line[i].h;
            pre_len = segTree[1].sum3;
        }
    }
    return res;
}

ll solve()
{
    ll w_x = depress_x();
    ll w_z = depress_z();
    ll res = 0;
    for (int i = 1; i <= w_z; i++)
    {
        build(1, 0, w_x);
        ll down_lim = new_z[i - 1];
        ll up_lim = new_z[i];
        res += (up_lim - down_lim) * square(down_lim, up_lim);
    }
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值