Title

(模板)线段树(未完结)

(模板)线段树

单点修改 + 区间查询(无\(tag\)

神奇的代码
#define int long long
using i64 = long long;
const int maxn = 5e5 + 5;
int nums[maxn];
int tree[4 * maxn];

void update(int root)
{
    int ch = root << 1;
    tree[root] = tree[ch] + tree[ch + 1];
}

void build(int root, int left, int right)
{
    if (left == right)
    {
        tree[root] = nums[left];
        return;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    build(ch, left, mid);
    build(ch + 1, mid + 1, right);
    update(root);
}

void modify(int pos, int root, int left, int right, int x)
{
    if (left == right)
    {
        tree[root] += x;
        return;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    if (pos <= mid)
    {
        modify(pos, ch, left, mid, x);
    }
    else
    {
        modify(pos, ch + 1, mid + 1, right, x);
    }
    update(root);
}

int range_query(int root, int left, int right, int x, int y)
{
    if (x <= left && right <= y)
    {
        return tree[root];
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    if (y <= mid)
    {
        return range_query(ch, left, mid, x, y);
    }
    if(x > mid)
    {
        return range_query(ch + 1, mid + 1, right, x, y);
    }
    return range_query(ch, left, mid, x, mid) + range_query(ch + 1, mid + 1, right, mid + 1, y);
}

区间修改 + 单点查询 + 区间查询(\(tag\)标记)

需要这样几个函数

\(pushdown\): 把标记传下去

神奇的代码
void pushdown(int root, int left, int right) // 把父亲的标记传给儿子
{
    int tot = right - left + 1;
    if (lazy[root] != 0)
    {
        int ch = root << 1;
        tree[ch] += (tot - tot / 2) * lazy[root];
        tree[ch + 1] += (tot / 2) * lazy[root];
        lazy[ch] += lazy[root];
        lazy[ch + 1] += lazy[root]; 
        lazy[root] = 0;
    }
}

为何\(pushdown\)的时候要改变结点的值?
\(lazy\)标记: 在更新区间的时候,为了方便,只更新了这个完整的区间,它的子区间未被更新而是打上了标记,因此向下一层传标记的时候这一层要改变

\(update\):由儿子更新父亲

神奇的代码
void update(int root, int left, int right)
{
    int ch = root << 1;
    int mid = (left + right) >> 1;
    pushdown(ch, left, mid);
    pushdown(ch + 1, mid + 1, right);
    tree[root] = tree[ch] + tree[ch + 1];
}

\(build\): 建树

神奇的代码
void build(int root, int left, int right)
{
    if (left == right)
    {
        tree[root] = nums[left];
        return;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    build(ch, left, mid);
    build(ch + 1, mid + 1, right);
    update(root, left, right);
}

\(range\_modify\): 区间修改

神奇的代码
void range_modify(int root, int left, int right, int x, int y, int val)
{
    pushdown(root, left, right);
    if (x <= left && right <= y)
    {
        lazy[root] += val;
        tree[root] += (right - left + 1) * val;
        return;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    if (y <= mid)
    {
        range_modify(ch, left, mid, x, y, val);
    }
    else if (x > mid)
    {
        range_modify(ch + 1, mid + 1, right, x, y, val);
    }
    else
    {
        range_modify(ch, left, mid, x, mid, val);
        range_modify(ch + 1, mid + 1, right, mid + 1, y, val);
    }
    update(root, left, right);
}

注意:这里区间修改为什么要\(pushdown\)呢,查询的时候直接\(pushdown\)不就行了吗? 有可能有这样的情况

    1. \(+3\)
    1. 变为\(x\)

和这样的情况

    1. 变为\(x\)
    1. \(+3\)

就会出问题了。当\(tag\)具有一定的时序关系的时候一定要在修改的时候\(pushdown\)一下

\(range\_query\): 区间查询

神奇的代码
int range_query(int root, int left, int right, int x, int y)
{
    pushdown(root, left, right);
    if (x <= left && right <= y)
    {
        return tree[root];
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    if (y <= mid)
    {
        return range_query(ch, left, mid, x, y);
    }
    if(x > mid)
    {
        return range_query(ch + 1, mid + 1, right, x, y);
    }
    return range_query(ch, left, mid, x, mid) + range_query(ch + 1, mid + 1, right, mid + 1, y);
}

区间修改 + 区间查询(多\(tag\))

这里是加乘线段树

对于多\(tag\),要有面向对象的思想,将\(tag\)也看成一个对象

需要这么几个函数

\(init\_lazy\): 初始化标记

神奇的代码
// 初始化lazy标记
void init_lazy(int root)
{
    multi[root] = 1;
    plus[root] = 0;
}

\(unionj\_lazy\)合并标记

神奇的代码
// 合并标记
void union_lazy(int root, int ch)
{
    int tmp1 = multi[root] * multi[ch];
    int tmp2 = multi[root] * plus[ch] + plus[root]; 
    multi[ch] = tmp1;
    plus[ch] = tmp2;
}

\(cal\_lazy\)计算标记

神奇的代码
// 计算标记
void cal_lazy(int root, int left, int right)
{
    int tot = right - left + 1;
    tree[root] = multi[root] * tree[root] + tot * plus[root];
}

不要犯傻 : 这里就乘了一次是因为乘法分配律

接下来其余的函数和以前一样,但是有些小变化

\(pushdown\):传标记

神奇的代码
void pushdown(int root, int left, int right) // 把父亲的标记传给儿子
{
    if (plus[root] != 0 || multi[root] != 1)
    {
        cal_lazy(root, left, right);
        int ch = root << 1;
        union_lazy(root, ch);
        union_lazy(root, ch + 1);
        init_lazy(root);
    }
}

\(update\):用子区间更新父亲区间

\(update\)变化不大

神奇的代码
void update(int root, int left, int right)
{
    int ch = root << 1;
    int mid = (left + right) >> 1;
    pushdown(ch, left, mid);
    pushdown(ch + 1, mid + 1, right);
    tree[root] = tree[ch] + tree[ch + 1];
}

\(build\): 建树

这个也没啥变化

神奇的代码
void build(int root, int left, int right)
{
    init_lazy(root);
    if (left == right)
    {
        tree[root] = nums[left];
        return;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    build(ch, left, mid);
    build(ch + 1, mid + 1, right);
    update(root, left, right);
}

\(range\_modify\) 区间修改

神奇的代码

void range_modify(int root, int left, int right, int x, int y, int val, int op)
{
    pushdown(root, left, right);
    if (x <= left && right <= y)
    {
        if (op == 1) // 乘法标记
        {
            multi[root] = val;
        }
        else
        {
            plus[root] = val;
        }
        return;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    if (y <= mid)
    {
        range_modify(ch, left, mid, x, y, val, op);
    }
    else if (x > mid)
    {
        range_modify(ch + 1, mid + 1, right, x, y, val, op);
    }
    else
    {
        range_modify(ch, left, mid, x, mid, val, op);
        range_modify(ch + 1, mid + 1, right, mid + 1, y, val, op);
    }
    update(root, left, right);
}

\(range\_query\) 区间查询

神奇的代码

int range_query(int root, int left, int right, int x, int y)
{
    pushdown(root, left, right);
    if (x <= left && right <= y)
    {
        return tree[root] % mod;
    }
    int ch = root << 1;
    int mid = (left + right) >> 1;
    if (y <= mid)
    {
        return range_query(ch, left, mid, x, y) % mod;
    }
    if(x > mid)
    {
        return range_query(ch + 1, mid + 1, right, x, y) % mod;
    }
    return ((range_query(ch, left, mid, x, mid) % mod) + (range_query(ch + 1, mid + 1, right, mid + 1, y) % mod)) % mod;
}

posted @ 2024-11-11 22:39  栗悟饭与龟功気波  阅读(0)  评论(0编辑  收藏  举报