HDU1166(线段树 +更新单点,求区间总和)、HDU1754(线段树 + 更新单点,求区间最大值)
线段树简单应用
- 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);   //⚠当前的这个下推语句在这个一次查询的时候不会被执行(由于查询区间的限制),但在以后的查询中 起着更新 子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;
}
HDU1166(线段树 +更新单点,求区间总和)、HDU1754(线段树 + 更新单点,求区间最大值)的更多相关文章
- HDU(1166),线段树模板,单点更新,区间总和
		题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 第一次做线段树,帆哥的一句话,我记下来了,其实,线段树就是一种处理数据查询和更新的手段. 然后, ... 
- CSU-1110 RMQ with Shifts (单点更新+区间最小值 zkw线段树)
		In the traditional RMQ (Range Minimum Query) problem, we have a static array A. Then for each query ... 
- HDU.1689 Just a Hook (线段树 区间替换 区间总和)
		HDU.1689 Just a Hook (线段树 区间替换 区间总和) 题意分析 一开始叶子节点均为1,操作为将[L,R]区间全部替换成C,求总区间[1,N]和 线段树维护区间和 . 建树的时候初始 ... 
- POJ 2528 Mayor's posters(线段树区间染色+离散化或倒序更新)
		Mayor's posters Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 59239 Accepted: 17157 ... 
- HDU 1540 Tunnel Warfare(经典)(区间合并)【线段树】
		<题目链接> 题目大意: 一个长度为n的线段,下面m个操作 D x 表示将单元x毁掉 R 表示修复最后毁坏的那个单元 Q x 询问这个单元以及它周围有多少个连续的单元,如果它本身已经被 ... 
- HDU 3308 LCIS (经典区间合并)【线段树】
		<题目链接> 题目大意: 给你一段序列,对其进行两种操作,一是修改某个序号的点的值:二是查询某个区间的LCIS(最长上升子序列). 解题分析: 线段树区间合并的典型例题,用求某个区间的LC ... 
- HDU1255 覆盖的面积 —— 求矩形交面积 线段树 + 扫描线 + 离散化
		题目链接:https://vjudge.net/problem/HDU-1255 给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input输入数据的第一行是一个正整数T(1<= ... 
- 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理  or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)
		Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ... 
- HDU1542 Atlantis —— 求矩形面积并 线段树 + 扫描线 + 离散化
		题目链接:https://vjudge.net/problem/HDU-1542 There are several ancient Greek texts that contain descript ... 
随机推荐
- SpringCloud第二代实战系列:一文搞定Nacos实现服务注册与发现
			一.背景:SpringCloud 生态圈 在正式开始本篇文章之前我们先岔开来讲一下SpringCloud的生态圈. SpringCloud大家都比较熟悉了,它制定了分布式系统的标准规范,做了高度抽象和 ... 
- vue iview modal弹出框 form表单验证
			一.ref="addApply" :model="addApply" :rules="ruleValidate" 不要忘记prop 二. ... 
- 03 HDFS的客户端操作
			服务器和客户端的概念 hdfs的客户端有多种形式 1.网页形式 2.命令行形式 3.客户端在哪里运行,没有约束,只要运行客户端的机器能够跟hdfs集群联网 参数配置 文件的切块大小和存储的副本数量,都 ... 
- 18 JpaRepository和JpaSpecificationExecutor
			继承JpaRepository后的方法列表 JpaRepository findAll() List<T> findAll(Sort) List<T> findAll(Iter ... 
- 备份Oracl数据库.bat
			=========================== @echo off echo ================================================ echo Win ... 
- Docker 使用杂记 - 最佳实践尝试 - 实战
			目录 Docker 使用杂记 - 最佳实践尝试 - 实战 Docker简介 项目背景 内在原因 外在原因 基础镜像 需求 镜像维护者 工作文件夹 文件 ADD COPY 宗卷 命令 入口点 Docke ... 
- 建议13:禁用Function构造函数
			定义函数的方法包括3种:function语句,Function构造函数和函数直接量.不管用哪种方法定义函数,它们都是Function对象的实例,并将继承Function对象所有默认或自定义的方法和属性 ... 
- react / config\webpack.config.js 编译后去掉map 减小体积 shouldUseSourceMap = false
			react / config\webpack.config.js 编译后去掉map 减小体积 shouldUseSourceMap = false 
- 【vue生命周期】- 详解
			这篇文章通俗易懂,写的不错,本文转载至:https://www.cnblogs.com/happ0/p/8075562.html 详解Vue Lifecycle 先来看看vue官网对vue生命周期的介 ... 
- 阅读GitHub源码的正确打开方式
			前言 近来发现阅读开源项目上手就整最新的代码不合适,缺少项目迭代的具体实现过程,想着若是可以看到针对问题的提交代码就好了,所以就有了本篇博客. 以文主要涉及:如何fork开源项目,如何保证本地仓库代码 ... 
 
			
		




