树状数组的建树 单点修改 单点查询 区间修改 区间查询

本文深入讲解线段树的构建、单点查询、区间查询及区间修改等操作,并提供详细的C++代码实现,涵盖懒惰传播优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单点修改  单点查询   用普通数组就能写出来  

单点修改  区间查询   用线段树  树状数组;

区间修改  区间查询   用线段树  树状数组;

区间修改  单点查询   用线段树  树状数组;

 

建树  

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
struct node
{
    int l,r,w;
}tree[4*maxn+1];
void build(int l,int r,int k)
{
    tree[k].l=l; tree[k].r=r;
    if(l==r)  { cin>>tree[k].w; return ; }
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
    tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
int main()
{
    build(1,8,1);
}
建树

单点查询

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
struct node
{
    int l,r,w;
}tree[4*maxn+1];
void build(int l,int r,int k)
{
    tree[k].l=l; tree[k].r=r;
    if(l==r)  { cin>>tree[k].w; cout<<l<<"--"<<tree[k].w<<endl; return ; }
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
    tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
int ask(int x,int k)// dian dian cha xun;
{
   if(tree[k].l==tree[k].r) {return tree[k].w; }
   int mid=(tree[k].l+tree[k].r)/2;
   if(x<=mid) return ask(x,2*k);
   else       return ask(x,2*k+1);

}
int main()
{
    build(1,8,1);
    //for(int i=1;i<=8;i++) cout<<"=="<<ask(i)<<endl;
    cout<<ask(1,1)<<endl;
    cout<<ask(7,1)<<endl;
}
单点查询

 单点修改(改变一个节点的值,在一个节点上加值)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
struct node
{
    int l,r,w;
}tree[4*maxn+1];
void build(int l,int r,int k)
{
    tree[k].l=l; tree[k].r=r;
    if(l==r)  { cin>>tree[k].w; /*cout<<l<<"--"<<tree[k].w<<endl;*/ return ; }
    int mid=(l+r)/2;
    build(l,mid,2*k);
    build(mid+1,r,2*k+1);
    tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
int ask(int x,int k)// dian dian cha xun;
{
   if(tree[k].l==tree[k].r) {return tree[k].w; }
   int mid=(tree[k].l+tree[k].r)/2;
   if(x<=mid) return ask(x,2*k);
   else       return ask(x,2*k+1);

}
void change(int x,int y,int k)// 将 节点 x  修改为y
{
    if(tree[k].l==tree[k].r){ tree[k].w=y; return;  }
    int mid=(tree[k].l+tree[k].r)/2;
    if(x<=mid) return change(x,y,2*k);
    else       return change(x,y,2*k+1);
}
void changeodd(int x,int y,int k)// 将 节点 x  修改为y
{
    if(tree[k].l==tree[k].r){ tree[k].w+=y; return;  }
    int mid=(tree[k].l+tree[k].r)/2;
    //cout<<"  "<<tree[k].l<<"  "<<tree[k].r<<endl; cout<<mid<<endl;
    if(x<=mid) return changeodd(x,y,2*k);
    else       return changeodd(x,y,2*k+1);
}
int main()
{
    build(1,8,1);
    change(1,10,1);
    changeodd(1,10,1);
    cout<<ask(1,1)<<endl;
    cout<<ask(7,1)<<endl;
}
单点查询

 

 

模板  没有区间修改下的区间查询 和单点查询 。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5;
struct node
{
int l,r,w;
}tree[4*maxn+1];
void build(int l,int r,int k)
{
tree[k].l=l; tree[k].r=r;
if(l==r) { cin>>tree[k].w; /*cout<<l<<"--"<<tree[k].w<<endl;*/ return ; }
int mid=(l+r)/2;
build(l,mid,2*k);
build(mid+1,r,2*k+1);
tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
int ask(int x,int k)// dian dian cha xun;
{
if(tree[k].l==tree[k].r) {return tree[k].w; }
int mid=(tree[k].l+tree[k].r)/2;
if(x<=mid) return ask(x,2*k);
else return ask(x,2*k+1);

}
void change(int x,int y,int k)// 将节点x修改为y
{
if(tree[k].l==tree[k].r){ tree[k].w=y; return; }
int mid=(tree[k].l+tree[k].r)/2;
if(x<=mid) change(x,y,2*k);
else change(x,y,2*k+1);
tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
void changeodd(int x,int y,int k)// 将节点 x 上加y
{
if(tree[k].l==tree[k].r){ tree[k].w+=y; return; }
int mid=(tree[k].l+tree[k].r)/2;
//cout<<" "<<tree[k].l<<" "<<tree[k].r<<endl; cout<<mid<<endl;
if(x<=mid) changeodd(x,y,2*k);
else changeodd(x,y,2*k+1);
tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
int query(int x,int y,int k) // 区间查询
{
int num=0;
if(tree[k].l==x && tree[k].r==y)
{
num+=tree[k].w; ; return num;
}
int mid=(tree[k].l+tree[k].r)/2;
if(x>=mid+1) num+=query(x,y,2*k+1);
else if(y<=mid) num+=query(x,y,2*k);
else num+=query(x,mid,2*k)+query(mid+1,y,2*k+1);

return num;
}int main()

{
build(1,8,1);
for(int i=1;i<=8;i++) {cout<<ask(1,1)<<" "; } cout<<endl;
cout<<query(1,7,1)<<endl;;
}

 区间修改(lazy标记)

 

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
struct NODE
{
int l,r,w,f; // f 为lazy标记
}tree[4*maxn+10];
void build(int p,int q,int k)
{
tree[k].l=p; tree[k].r=q;
if(tree[k].l==tree[k].r) { cin>>tree[k].w;/* cout<<"==="<<tree[k].w<<endl;*/ return ; }
int mid=( tree[k].l+tree[k].r )/2;
build(p,mid,2*k);
build(mid+1,q,2*k+1);
tree[k].w=tree[2*k].w+tree[2*k+1].w;
}
void down(int k) // (lazy 标记) (××××) (important)
{
tree[2*k].f+=tree[k].f;
tree[2*k+1].f+=tree[k].f;
tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
tree[k].f=0;
}
void change_interval(int a,int b,int x,int k) // 区间修改 区间加值;
{
if(tree[k].l>=a && tree[k].r<=b )
{
tree[k].w+=(tree[k].r-tree[k].l+1)*x;
tree[k].f+=x;
return;
}
if(tree[k].f) down(k);
int mid=(tree[k].l+tree[k].r)/2;
if(b<=mid) change_interval(a,b,x,2*k);
else if(a>=mid+1) change_interval(a,b,x,2*k+1);
else change_interval(a,mid,x,2*k),change_interval(mid+1,b,x,2*k+1);
tree[k].w=tree[k*2].w+tree[k*2+1].w;
}
void change_point(int x,int k)
{
if(tree[k].l==tree[k].r) { tree[k].w+=x; return; }
if(tree[k].f) down(k);
int mid=(tree[k].l+tree[k].r)/2;
if(x<=mid) change_point(x,2*k);
else change_point(x,2*k+1);
tree[k].w=tree[k*2].w+tree[k*2+1].w;
}
int ask_interval(int a,int b,int k) // 区间查询
{
int num=0;

if(tree[k].l>=a&&tree[k].r<=b)
{
num+=tree[k].w;
return num;
}
if(tree[k].f) down(k);
int mid=( tree[k].l+tree[k].r)/2;
if(b<=mid) num+=ask_interval(a,b,2*k);
else if(a>=mid+1) num+=ask_interval(a,b,2*k+1);
else num+=ask_interval(a,mid,2*k)+ask_interval(mid+1,b,2*k+1);
return num;
}
int ask_point(int x,int k) // 点查询
{
int num=0;
if(tree[k].l==tree[k].r) {return tree[k].w; }
if(tree[k].f) down(k);
int mid=(tree[k].l+tree[k].r)/2;
if(x<=mid) ask_point(x,2*k);
if(x>=mid+1) ask_point(x,2*k+1);
}
int32_t main()
{
}

 

转载于:https://www.cnblogs.com/Andromeda-Galaxy/p/9715315.html

<think>嗯,用户需要一个C++实现的线段树模板,支持区间修改区间查询。我得先回忆一下线段树的基本结构。线段树通常用结构体或类来实现,每个节点包含区间范围、左右子节点指针或数组索引,以及需要维护的数据,比如区间和、最大值等。还有,区间修改需要用到懒标记(lazy tag),这样能延迟更新,提高效率。 用户提到的引用[2]和[3]中提到,线段树的区间修改查询需要处理lazy tag。比如区间加和或者异或操作,这里用户可能需要的是通用的区间修改,比如加法操作。所以模板应该包含区间加法的懒标记处理。 首先,线段树的构建。通常用递归方法,当区间长度为1时,直接赋值;否则分割成左右子区间,递归构建左右子树。然后合并信息,比如区间和是左右子树的和之和。 然后是下推懒标记(push_down函数)。当当前节点有未处理的修改时,需要将标记传递给子节点,并更新子节点的值和懒标记。这里要注意,如果有多个懒标记类型(比如加法和乘法),处理顺序会有影响,但用户可能只需要加法,所以暂时处理加法的情况。 接下来是区间更新(update函数)。如果当前节点的区间完全在目标区间内,就更新当前节点的值并打上懒标记。否则,需要先下推现有的懒标记,然后根据左右子节点的情况递归更新。 区间查询(query函数)也类似,如果当前节点区间完全在查询区间内,直接返回值;否则下推懒标记后,分别查询左右子节点并合并结果。 引用[3]提到C++用类实现线段树模板,支持多种操作,比如区间求和、最大值,以及区间修改。所以代码结构应该是一个类,包含构建、更新、查询的方法。需要模板类吗?可能用户希望数据类型是灵活的,比如int或long long,所以模板参数可能有用。 用户可能需要完整的代码示例,所以需要提供一个具体的实现。比如,使用类成员变量存储左右边界、左右子节点索引、值、懒标记等。或者,使用数组来存储线段树的节点,这样可能更高效,但动态分配可能麻烦。另一种方法是使用结构体,每个节点包含必要的信息。 考虑到代码的简洁性,或许用数组存储线段树节点会更方便,但动态扩展可能有问题。或者,使用vector预先分配足够的空间,比如4倍原始数据大小,这是常见的做法。 例如,数据存储在一个vector中,每个节点包含l, r, sum, add标记。在建树时递归初始化这些值。update和query的时候处理区间覆盖和懒标记的下推。 现在,我需要检查代码是否符合用户的需求。用户要求区间修改区间查询,所以代码中的update函数应该处理区间加法,query函数返回区间和。同时,要处理懒标记的正确下推。 可能还需要测试用例,比如构造一个数组,进行区间加法和查询,验证结果是否正确。但用户可能只需要模板代码,测试用例可以省略。 另外,引用[3]中提到,如果只需要区间和、加法、单点修改树状数组可能更高效,但用户明确需要线段树的模板,所以不用考虑树状数组。 总结:代码结构应该是类或结构体,包含建树、更新、查询、下推标记等方法。使用数组或vector存储节点,每个节点有区间范围、sum值、add标记。区间修改使用加法,支持懒标记的延迟更新。 现在,编写具体的代码。例如,使用类SegmentTree,构造函数接受数据数组。内部节点用结构体或数组存储。由于C++中动态分配子节点可能复杂,用数组索引的方式可能更简单,预先分配足够大的空间。 或者,采用堆式存储,即用数组表示二叉树,左子节点为2*i+1,右子节点为2*i+2。但这样空间可能浪费。另一种方法是动态创建节点,使用指针,但可能更复杂。 综合考虑,可能更简单的是预先分配一个足够大的数组,比如4*n的大小,其中n是原始数据的大小。每个节点包含l, r, sum, add。例如: struct Node { int l, r; int sum; int add; } tr[N * 4]; 然后用递归函数建树。update和query时处理区间分割和懒标记。 但用户可能需要一个完整的类实现,所以将线段树封装成类,内部使用数组存储节点,或者用vector。 例如: class SegmentTree { private: struct Node { int l, r; int sum; int add; }; vector<Node> tr; vector<int> nums; void push_up(int u) { tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum; } void push_down(int u) { if (tr[u].add) { int add = tr[u].add; tr[u<<1].sum += add * (tr[u<<1].r - tr[u<<1].l + 1); tr[u<<1|1].sum += add * (tr[u<<1|1].r - tr[u<<1|1].l + 1); tr[u<<1].add += add; tr[u<<1|1].add += add; tr[u].add = 0; } } void build(int u, int l, int r) { tr[u] = {l, r, 0, 0}; if (l == r) { tr[u].sum = nums[l]; return; } int mid = (l + r) >> 1; build(u<<1, l, mid); build(u<<1|1, mid+1, r); push_up(u); } void update(int u, int l, int r, int add) { if (tr[u].l >= l && tr[u].r <= r) { tr[u].sum += add * (tr[u].r - tr[u].l + 1); tr[u].add += add; return; } push_down(u); int mid = (tr[u].l + tr[u].r) >> 1; if (l <= mid) update(u<<1, l, r, add); if (r > mid) update(u<<1|1, l, r, add); push_up(u); } int query(int u, int l, int r) { if (tr[u].l >= l && tr[u].r <= r) { return tr[u].sum; } push_down(u); int mid = (tr[u].l + tr[u].r) >> 1; int sum = 0; if (l <= mid) sum += query(u<<1, l, r); if (r > mid) sum += query(u<<1|1, l, r); return sum; } public: SegmentTree(vector<int>& nums) : nums(nums) { int n = nums.size(); tr.resize(n * 4); build(1, 0, n - 1); } void update_range(int l, int r, int add) { update(1, l, r, add); } int query_range(int l, int r) { return query(1, l, r); } }; 不过,这里可能存在一些问题。比如,数组索引是否从1开始?在build函数中,u<<1和u<<1|1需要确保不会越界。此外,构造函数中的tr.resize(n*4)可能不够,需要确保足够的空间。或者,采用动态分配的方式,但可能比较复杂。 另外,在构造函数中,调用build(1, 0, n-1),假设根节点是1,左子节点是2,右是3,依此类推。这样,如果n很大,n*4是否足够?例如,当n是1e5时,tr的大小是4e5,这通常是足够的。 但需要确认线段树的数组大小是否正确。一般来说,线段树的大小是4*n,足够存储所有节点。因此,这种实现方式是可行的。 这样,用户可以使用这个类来初始化线段树,然后调用update_range和query_range进行区间修改查询。 不过,用户可能需要更详细的注释或者说明,比如每个函数的作用,以及如何调用。因此,在代码中添加注释是必要的。 最后,根据用户提供的引用,尤其是引用[2]和[3],需要确保代码符合区间修改查询的标准实现,使用懒标记,并正确处理各种情况。例如,push_down函数的正确性,以及在update和query时递归调用的条件是否正确。 综上所述,这样的代码应该满足用户的需求,提供一个C++实现的线段树模板,支持区间加法的修改区间和的查询。</think>以下是一个支持区间加法修改区间求和的线段树C++模板代码实现,基于懒标记(lazy tag)技术: ```cpp #include <vector> using namespace std; class SegmentTree { private: struct Node { int l, r; // 区间端点 int sum; // 区间和 int add; // 懒标记(区间增量) }; vector<Node> tr; // 线段树数组 vector<int> nums; // 原始数据 // 更新父节点 void push_up(int u) { tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; } // 下传懒标记 void push_down(int u) { if (tr[u].add) { int mid = (tr[u].l + tr[u].r) >> 1; // 更新左子树 tr[u << 1].sum += tr[u].add * (mid - tr[u].l + 1); tr[u << 1].add += tr[u].add; // 更新右子树 tr[u << 1 | 1].sum += tr[u].add * (tr[u].r - mid); tr[u << 1 | 1].add += tr[u].add; tr[u].add = 0; // 清除标记 } } // 建树 void build(int u, int l, int r) { tr[u] = {l, r, 0, 0}; if (l == r) { tr[u].sum = nums[l]; return; } int mid = (l + r) >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); push_up(u); } // 区间修改 void update(int u, int l, int r, int add) { if (tr[u].l >= l && tr[u].r <= r) { tr[u].sum += add * (tr[u].r - tr[u].l + 1); tr[u].add += add; return; } push_down(u); int mid = (tr[u].l + tr[u].r) >> 1; if (l <= mid) update(u << 1, l, r, add); if (r > mid) update(u << 1 | 1, l, r, add); push_up(u); } // 区间查询 int query(int u, int l, int r) { if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum; push_down(u); int mid = (tr[u].l + tr[u].r) >> 1; int sum = 0; if (l <= mid) sum += query(u << 1, l, r); if (r > mid) sum += query(u << 1 | 1, l, r); return sum; } public: SegmentTree(vector<int>& arr) : nums(arr) { int n = nums.size(); tr.resize(n * 4); // 预分配4倍空间 build(1, 0, n - 1); // 根节点从索引1开始 } // 对外接口:区间加法 void range_add(int l, int r, int val) { update(1, l, r, val); } // 对外接口:区间求和 int range_query(int l, int r) { return query(1, l, r); } }; ``` ### 使用示例 ```cpp int main() { vector<int> arr = {1,3,5,7,9}; SegmentTree st(arr); st.range_add(1, 3, 2); // 区间[1,3]每个元素+2 cout << st.range_query(0, 4); // 查询整个区间和 return 0; } ``` ### 代码特点 1. **时间复杂度**:建树$O(n)$,查询/修改操作$O(\log n)$ 2. **空间复杂度**:$O(4n)$预分配内存 3. **懒标记机制**:通过`push_down`实现延迟更新,确保时间复杂度最优[^2] 4. **区间操作**:支持任意区间的加法修改和求和查询 对于只需要单点修改的场景,可以通过调用`range_add(pos, pos, val)`实现。若需要支持其他区间操作(如区间乘、区间最值),需要调整节点存储的信息和懒标记处理逻辑[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值