AcWing算法提高课 线段树

操作:

pushup:由子节点算父节点信息

build:建立线段树,将一段区间初始化为线段树

modify:修改,修改某个点,或修改某个区间(需要使用pushdown)

query:查询,查询某一段区间的信息

pushdown:父节点修改传递到子节点修改(懒标记)

原理:

满二叉树:

 

 节点编号:

 

一般数组开4n的大小 

模板:

仅支持单点修改:(以记录最大值为例)

复制代码
const int N = 200010;
struct Node
{
    int l, r;
    int val;
}tr[N * 4];

//由子节点信息计算父节点信息,以最大值为例
void pushup(int u)
{
    tr[u].val = max(tr[2 * u].val, tr[2 * u + 1].val);
}

void build(int u, int l, int r)
{

    if (l == r)
    {
        tr[u] = { l,r,0 };
    }
    else
    {
        tr[u].l = l; tr[u].r = r;
        int mid = (l + r) / 2;
        build(2 * u, l, mid);
        build(2 * u + 1, mid + 1, r);
        pushup(u);
    }

}

int query(int u, int l, int r)
{
    if (l > r)
        return 0;
    if (tr[u].l >= l && tr[u].r <= r)
    {
        return tr[u].val;
    }
    int mid = (tr[u].l + tr[u].r) / 2;
    int res = 0;
    if (l <= mid) res = query(2 * u, l, r);
    if (r > mid) res = max(res, query(2 * u + 1, l, r));
    return res;
}
void modify(int u, int x, int val)//修改x的信息
{
    if (tr[u].l == x && tr[u].r == x) tr[u].val = val;
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid)
            modify(2 * u, x, val);
        else
            modify(2 * u + 1, x, val);
        pushup(u);//注意更新
    }
}


build(1, 1, n);

for (int i = 1; i <= n; i++)
{
    int a;
    cin >> a;
    modify(1, i, a);
}
View Code
复制代码

 

例题:

 245. 你能回答这些问题吗(线段树维护连续子区间最大值)

复制代码
#include<bits/stdc++.h>
#include<unordered_set>
using namespace std;
typedef long long LL;
struct Node 
{
    int l,r;
    int imax;
    int lmax, rmax;
    int sum;
}tr[500010*4];
int n, m, nums[500010];

void pushup(int u)
{
    int l = 2 * u, r = 2 * u + 1;
    tr[u].imax = max({ tr[l].rmax + tr[r].lmax, tr[l].imax , tr[r].imax });
    tr[u].lmax = max(tr[l].lmax, tr[l].sum + tr[r].lmax);
    tr[u].rmax = max(tr[r].rmax, tr[r].sum + tr[l].rmax);
    tr[u].sum = tr[l].sum + tr[r].sum;
}

void build(int u, int l, int r)
{

    if (l == r)
    {
        tr[u] = { l,r,nums[l],nums[l] ,nums[l] ,nums[l] };

    }
    else
    {
        tr[u].l = l; tr[u].r = r;
        int mid = l + r >> 1;
        build(u *2, l, mid);
        build(u *2 + 1, mid + 1, r);
        pushup(u);
    }

}

void modify(int u, int i,int v)
{
    if (tr[u].l == i && tr[u].r == i)
    {
        tr[u] = { i,i,v,v,v,v };
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (i <= mid) modify(2 * u, i, v);
        else modify(2 * u + 1, i, v);
        pushup(u);
    }
}

Node query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r)
    {
        return tr[u];
    }
    else
    {
        int mid = tr[u].l + tr[u].r  >> 1;
        if (r <= mid)
            return query(2 * u, l, r);
        else if (l > mid)
            return query(2 * u + 1, l, r);
        else
        {
            auto left= query(2 * u, l, r);
            auto right= query(2 * u + 1, l, r);
            Node res;
            //res.l = l; res.r = r;
            res.imax = max({ left.rmax + right.lmax, left.imax , right.imax });
            res.lmax = max(left.lmax, left.sum + right.lmax);
            res.rmax = max(right.rmax, right.sum + left.rmax);
            res.sum = left.sum + right.sum;
            return res;
        }

    }
}
void YD()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> nums[i];
    build(1, 1, n);
    int k, x, y;
    while (m--)
    {
        cin >> k >> x >> y;
        if (k == 1)
        {
            if (x > y) swap(x, y);
            cout << query(1, x, y).imax << endl;
        }
        else
        {
            modify(1, x, y);
        }
    }
}

int main()
{
    YD();
    return 0;
}
View Code
复制代码

注意,由于左移、右移操作的优先级比加法低,所以要么2*u+1,要么u>>1|1,不能把>>和+混用

 

AcWing 246. 区间最大公约数 (线段树维护最大公约数,区间整体加一个数)

此题虽然做了区间修改,但仅会区间整体加一个数,可以转化为差分数组的单点修改,所以不用懒标记

数组的最大公约数和差分数组的最大公约数也有转化的关系

故只需要对差分数组进行单点修改和区间查询

记录的值为差分数组的最大公约数和区间和(差分的前缀和用于求单点的值)

复制代码
#include<bits/stdc++.h>
#include<unordered_set>
using namespace std;
typedef long long LL;
const int N = 500010;

int n,m;
LL nums[N];
struct Node
{
    int l, r;
    LL sum, d;
}tr[N*4];

LL GCD(LL a, LL b)
{
    return b == 0 ? a : GCD(b, a % b);

}
void pushup(int u)
{
    tr[u].sum = tr[2 * u].sum + tr[2 * u + 1].sum;
    tr[u].d= GCD(tr[2 * u].d , tr[2 * u + 1].d);
}
void build(int u, int l, int r)
{
    if (l == r)
    {
        LL t = nums[l] - nums[l - 1];
        tr[u] = { l,r,t,t };
    }
    else
    {
        tr[u].l = l; tr[u].r = r;
        int mid = l + r >> 1;
        build(u * 2, l, mid);
        build(u * 2 + 1, mid + 1, r);
        pushup(u);
    }
}
void modify(int u, int i, LL v)
{
    if (tr[u].l == i && tr[u].r == i)
    {
        //单点
        tr[u].sum += v;
        tr[u].d += v;
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (i <= mid) modify(2 * u, i, v);
        else modify(2 * u+1, i, v);
        pushup(u);
    }
}
Node query(int u, int l, int r)
{
    if (l <= tr[u].l && r >= tr[u].r)
    {
        return tr[u];
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if (mid >= r) return query(2 * u, l, r);
        else if (mid < l) return query(2 * u + 1, l, r);
        else
        {
            Node left= query(2 * u, l, r);
            Node right= query(2 * u + 1, l, r);
            Node res;
            res.sum = left.sum + right.sum;
            res.d = GCD(left.d, right.d);
            return res;
        }
    }
}
void YD()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> nums[i];
    build(1, 1, n);

    int l, r;
    LL d;
    char op;
    while (m--)
    {
        cin >> op >> l >> r;

        if (op == 'Q')
        {
            auto left = query(1, 1, r);
            LL tmp = left.sum;
            if (l + 1 <= r)
            {
                tmp = GCD(tmp, query(1, l + 1, r).d);
            }
            cout << abs(tmp)<< endl;
        }
        else
        {
            LL v;
            cin >> v;
            modify(1, l, v);
            if(r+1<=n) modify(1, r + 1, -v);
        }
    }
}

int main()
{
    YD();
    return 0;
}
View Code
复制代码

 

posted @   80k  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示