HDU1166(线段树 +更新单点,求区间总和)、HDU1754(线段树 + 更新单点,求区间最大值)

线段树简单应用

先附上几张图便与理解,大佬文章传送门1传送门2
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • HDU1166:题目描述
    线段树 +更新单点,求区间总和

代码如下(递归版)

#include<iostream>
#include<string>
using namespace std;
#define MAXN 50005
#define ls l,m,pos << 1
#define rs m+1,r,pos << 1 | 1

int node[MAXN],Sum[MAXN << 2],Add[MAXN << 2];

//上推更新信息、建树
void Push_up(int pos)
{
    Sum[pos] = Sum[pos << 1] + Sum[pos << 1 | 1];
}

void Build(int l, int r, int pos)
{
    if(l == r)
    {
        Sum[pos] = node[l];
        return;
    }
    //左右递归区间
    int m = (l + r) >> 1;
    Build(ls);
    Build(rs);
    //更新信息
    Push_up(pos);
}

//点的修改
void Update_point(int l, int r, int pos, int x, int c)
{
    if(l == r)
    {
        Sum[pos] += c;
        return;
    }

    //看下标x 是在左子区间,还是在有子区间
    int m = (l + r) >> 1;
    if(x <= m) Update_point(ls, x, c);
    else       Update_point(rs, x, c);
    //回溯的时候从下往上更新 Sum
    Push_up(pos);
}


//下推做标记、区间的修改
void Push_down(int ln, int rn, int pos)
{
    if(Add[pos])
    {
        //向下标记子区间
        Add[pos << 1] += Add[pos];
        Add[pos << 1 | 1] += Add[pos];
        //更新sum值
        Sum[pos << 1] += Add[pos] * ln;
        Sum[pos << 1 | 1] += Add[pos] * rn;
        //解除当前的标记
        Add[pos] = 0;
    }
}

void Update_area(int l, int r, int pos, int s, int e, int c)
{
    if(s <= l && r <= e)
    {
        Sum[pos] += (r - l + 1) * c;
        Add[pos] += c;
        return;
    }
    //对左右区间进行讨论
    int m = (l + r) >> 1;
    //先下推标记,为更新本节点的 Sum 做准备
    Push_down(m - l + 1, r - m, pos);
    if(s <= m) Update_area(ls, s, e, c);
    if(e >  m) Update_area(rs, s, e, c);
    //上推更新当前的Sum,因为可能子Sum已经改变
    Push_up(pos);
}

int Query(int l, int r, int pos, int s, int e)
{
    if(s <= l && r <= e)
    {
        return Sum[pos];
    }

    //左右区间进行讨论,积累答案
    int m = (l + r) >> 1;
    Push_down(m - l + 1, r - m, pos);   //&#9888;当前的这个下推语句在这个一次查询的时候不会被执行(由于查询区间的限制),但在以后的查询中 起着更新 子Sum的作用(由于以前的某个/些 子区间没有被执行,所以当来到 这个区间的到时候我们要的是 已经更新过的 子区间Sum)
    int ans = 0;
    if(s <= m) ans += Query(ls, s, e);
    if(e >  m) ans += Query(rs, s, e);
    return ans;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(nullptr);
    //freopen("T.txt","r",stdin);
    string s1 = "Add";
    string s2 = "Sub";
    string s3 = "Query";
    string s4 = "End";
    string s;
    int t, Case = 1;
    cin >> t;
    while(t --)
    {
        cout << "Case "<<Case ++<<":" << endl;
        int n;
        cin >> n;
        for(int i = 1; i <= n; i ++)
            cin >> node[i];
        Build(1, n, 1);
        while(cin >> s && s != s4)
        {
            int a,b;
            cin >> a >> b;
            if(s == s1)
                Update_point(1, n, 1, a, b);
            else if(s == s2)
                Update_point(1, n, 1, a,-b);
            else
                cout << Query(1, n, 1, a, b) << endl;
        }
    }

    return 0;
}

代码如下(非递归版)

#include<iostream>
using namespace std;
#define MAXN 50005
#define ls l,m,pos << 1
#define rs m+1,r,pos << 1 | 1
int node[MAXN],Sum[MAXN << 2],Add[MAXN << 2];
int N,n;

//建立线段树
void Build(int n)
{
    //找n接近的 2 的次方数
    N = 1; while(N < n + 2) N <<= 1;
    //更新叶节点
    for(int i = 1; i <= n; i ++) Sum[i + N] = node[i];  //存储叶节点的下标 + N == 叶节点位于树中的下标 == 存储数据的 node 数组中的下标
    //更新非叶节点
    for(int i = N - 1; i > 0; i --)
    {
        //更新所有非叶节点的统计信息
        Sum[i] = Sum[i << 1] + Sum[i << 1 | 1];
        //清空所有非叶节点的标记
        Add[i] = 0;
    }
}
//点的修改
void Update_point(int x, int c)
{
    for(int pos = N + x; pos; pos >>= 1)
    {
        Sum[pos] += c;
    }
}
//没有标记下的区间查询
int Query(int s, int e)
{
    int ans = 0;
    for(int L = N + s - 1,R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1)
    {
        if(~ L & 1) ans += Sum[L ^ 1];
        if(  R & 1) ans += Sum[R ^ 1];
    }
    return ans;
}

//区间修改
void Update_area(int s, int e, int c)
{
    int L,R,Ln = 0,Rn = 0,x = 1;
    for(L = N + s -1, R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1, x <<= 1)
    {
        //更新Sum
        Sum[L] += c * Ln;
        Sum[R] += c * Rn;
        //处理Add标记
        if(~ L & 1) Add[L ^ 1] += c,Sum[L ^ 1] += c * x,Ln += x;
        if(  R & 1) Add[R & 1] += c,Sum[R & 1] += c & x,Rn += x;
    }
    //更新上层的Sum
    for( ; L; L >>= 1, R >>= 1)
    {
        Sum[L] += c * Ln;
        Sum[R] += c * Rn;
    }
}
//区间查询
int Query_area(int s, int e)
{
    int ans = 0;
    int L,R,Ln = 0,Rn = 0,x = 1;
    for(L = N + s - 1, R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1, x <<= 1)
    {
        //如果当前节点有标记(说明该节点的 子节点,子子节点 。。。。 是没有加上标记值的)
        ans += Add[L] * Ln;
        ans += Add[R] * Rn;

        //如果该节点是左子节点的左子树 或 右子节点的右子树
        if(~ L & 1) ans += Sum[L ^ 1], Ln += x;
        if(  R & 1) ans += Sum[R ^ 1], Rn += x;
    }
    //处理上层的标记
    for( ; L; L >>= 1, R >>= 1)
    {
        ans += Add[L] * Ln;
        ans += Add[R] * Rn;
    }
    return ans;
}


int main()
{

    ios::sync_with_stdio(false); cin.tie(nullptr);
    //freopen("T.txt","r",stdin);
    string s1 = "Add";
    string s2 = "Sub";
    string s3 = "Query";
    string s4 = "End";
    string s;
    int t, Case = 1;
    cin >> t;
    while(t --)
    {
        cout << "Case "<<Case ++<<":" << endl;
        cin >> n;
        for(int i = 1; i <= n; i ++)
            cin >> node[i];
        Build(n);
        while(cin >> s && s != s4)
        {
            int a,b;
            cin >> a >> b;
            if(s == s1)
                Update_point(a, b);
            else if(s == s2)
                Update_point(a,-b);
            else
                cout << Query_area(a, b) << endl;
        }
    }

    return 0;
}
  • HDU1754: 题目描述
    线段树 +更新单点,求区间最大值!!!

代码如下

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define ls l,m,pos << 1
#define rs m+1,r,pos << 1 | 1

int node[MAXN],Max[MAXN << 2],Add[MAXN << 2];

//向上传递更新信息、建树
void Push_up(int pos)
{
    Max[pos] = max(Max[pos << 1], Max[pos << 1 | 1]);
}

void Build(int l, int r, int pos)
{
    if(l == r)
    {
        Max[pos] = node[l];
        return;
    }

    //分左右子区间进行讨论
    int m = (l + r) >> 1;
    Build(ls);
    Build(rs);
    Push_up(pos);
}

void Update_point(int l, int r, int pos, int x, int c)
{
    if(l == r)
    {
        Max[pos] = c;
        return;
    }
    //分左右区间进行讨论
    int m = (l + r) >> 1;
    if(x <= m) Update_point(ls, x, c);
    else       Update_point(rs, x, c);
    Push_up(pos);
}

int Query(int l, int r, int pos, int s, int e)
{
    if(s <= l && r <= e)
    {
        return Max[pos];
    }

    //分左右区间
    int m = (l + r) >> 1;
    int mx = -1;
    if(s <= m) mx = max(mx, Query(ls, s, e));
    if(e >  m) mx = max(mx, Query(rs, s, e));
    return mx;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(nullptr);
    //freopen("T.txt","r",stdin);
    int n,m;
    while(cin >> n >> m)
    {
        for(int i = 1; i <= n; i ++)
            cin >> node[i];
        Build(1, n, 1);
        char ch;
        int a, b;
        while(m --)
        {
            cin >> ch >> a >> b;
            if(ch == 'Q')
                cout << Query(1, n, 1, a, b) << endl;
            else
                Update_point(1, n, 1, a, b);
        }
    }

    return 0;
}

代码如下(非递归版)

⚠️ 这个代码 超时了,也没想好怎么优化

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
int node[MAXN],Max[MAXN << 2];
int n,N;

//非递归线段树建树
void Build(int n)
{
    //找到一个 > n + 2 的 N
    N = 1; while(N < n + 2) N <<= 1;
    //处理叶节点的Max问题
    for(int i = 1; i <= n; i ++)
        Max[N + i] = node[i];
    //处理非叶节点的最大值
    for(int i = N - 1; i; i --)
        Max[i] = max(Max[i << 1], Max[i << 1 | 1]);
}

//更新某个节点的值
void Update_point(int x, int c)
{
    Max[N + x] = c;
    int i = N + x;
    for(i >>= 1; i; i >>= 1)
    {
        Max[i] = max(Max[i << 1], Max[i << 1 | 1]);
    }
}
int Query(int s, int e)
{
    int L,R,mx = -1;
    for(L = N + s - 1,R = N + e + 1; L ^ R ^ 1; L >>= 1, R >>= 1)
    {
        if(~ L & 1) mx = max(mx, Max[L ^ 1]);
        if(  R & 1) mx = max(mx, Max[R ^ 1]);
    }
    return mx;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(nullptr);
    //freopen("T.txt","r",stdin);
    int n,m;
    while(cin >> n >> m)
    {
        for(int i = 1; i <= n; i ++)
            cin >> node[i];
        Build(n);
        char ch;
        int a, b;
        while(m --)
        {
            cin >> ch >> a >> b;
            if(ch == 'Q')
                cout << Query(a, b) << endl;
            else
                Update_point(a, b);
        }
    }

    return 0;
}

发布了119 篇原创文章 · 获赞 186 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/104293614
今日推荐