基本概念
线段树的每个节点表示一个区间,主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。
- 每个节点的左孩子区间范围为[ l,mid ],右孩子为[ mid+1 , r ]
- 对于结点k,左孩子结点为2 * k,右孩子为2*k+1。
基本操作
struct node
{
int l,r; //l,r分别表示区间左右端点
int w; // w为你想要维护的东西,这里假装维护的是区间和
int add; // 懒标记(后面会解释)
}t[4*n+1];
状态回溯
这里维护区间和
void push_up(int rt)
{
t[rt].w=t[rt<<1].w+t[rt<<1|1].w;
}
建树
- 二分每个结点,给他左右端点的范围
- 找到叶子结点,赋值
- 状态合并
void build(int l,int r,int rt)
{
t[rt].l=l;
t[rt].r=r;
if(l==r) //叶子节点
{
scanf("%d",&t[rt].w); // 赋值
return ;
}
int m=(l+r)/2;
build(l,m,rt<<1);//递归左子树
build(m+1,r,rt<<1|1);//递归右子树
push_up(rt); //状态合并(回溯)
}
单点修改
- 二分寻找需要的点
- 修改后,要回溯修改他的祖先节点们
void updata_single(int rt,int a,int b)
/* rt为当前节点 a为待修改节点的编号,b为修改的值*/
{
if(t[rt].l==t[rt].r) // 找到需要修改的节点
{
t[i].w=b;
return ;
}
int mid=(t[rt].l+t[rt].r)>>1;
if(a<=mid) updata_single(rt<<1,a,b); //递归左子树
else updata_single(rt<<1|1,a,b); //递归右子树
push_up(rt); // 状态回溯
}
单点查询
方法同上面差不多
int quary_single(int rt,int a)
/* rt为当前节点 a为需要节点的编号*/
{
if(t[rt].l==t[rt].r) // 找到需要修改的节点
{
return t[i].w;
}
int mid=(t[rt].l+t[rt].r)>>1;
if(a<=mid) quary_single(rt<<1,a); //递归左子树
else quary_single(rt<<1|1,a); //递归右子树
}
区间修改
我们引入一个懒标记
- 存储到这个节点的修改信息,暂时不把修改信息传到子节点。
- 实现思路:
a.递归到这个节点时,只更新这个节点的状态,并把当前的更改值累积到标记中。
b.当需要递归这个节点的子节点时,将标记下传给子节点。
c.下传操作:
①当前节点的懒标记累积到子节点的懒标记中。
②修改子节点状态。在这里,就是原状态+子节点区间点的个数*父节点传下来的懒标记。
③父节点懒标记清0。这个懒标记已经传下去了,不清0后面再用这个懒标记时会重复下传。
当前节点的标志域向孩子节点传递
void push_down(int rt)
{
if(t[rt].add==0) return;
// 向孩子节点传递懒标记
tree[rt<<1].add+=tree[rt].add;
tree[rt<<1|1].add+=tree[rt].add;
//修改子节点状态
tree[rt<<1].w+=tree[rt].add*(tree[rt<<1].r-tree[rt<<1].l+1);
tree[rt<<1|1].w+=tree[rt].add*(tree[rt<<1|1].r-tree[rt<<1|1].l+1);
//父节点懒标记清0
tree[rt].add=0;
}
这里表示在区间[ x , y ]中的每一个数加上w
void update_interval(int rt, int l, int r, int x, int y, int w)
{
if(x>r||y<l) return ; //待修改区间和当前节点区间没有交集
if(x<=l&&r<=y) //当前节点区间包含在待修改区间内
{
t[rt].add+=w;
t[rt].w+=w;
return ;
}
push_down(rt); //延迟标记向下传递
int mid=(l+r)>>1;
//更新左右孩子节点
update_interval(rt>>1,l,mid,x,y,w);
update_interval(rt>>1|1,mid+1,r,x,y,w);
push_up(rt);
}
区间查询
int query_interval(int rt,int l,int r,int x,int y)
{
if(x>r||y<l) return 0; //查询区间和当前节点区间没有交集
if(x<=l&&r<=y) return t[rt].w; //当前节点区间包含在查询区间内
push_down(rt); //延迟标志域向下传递
int mid=(l+r)>>1;
return query_interval(rt<<1,l,mid,x,y)+query_interval(rt<<1|1,mid+1,r,x,y);
}
总和
#include<bits/stdc++.h>
using namespace std;
struct node
{
int l,r,w,add;
} t[4000010];
void push_up(int rt)
{
t[rt].w=t[rt<<1].w+t[rt<<1|1].w;
}
void build(int l,int r,int rt)
{
t[rt].l=l;
t[rt].r=r;
if(l==r)
{
scanf("%d",&t[rt].w);
return ;
}
int m=(l+r)/2;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
push_up(rt);
}
void push_down(int rt)
{
if(t[rt].add==0) return;
tree[rt>>1].add+=tree[rt].add;
tree[rt>>1|1].add+=tree[rt].add;
tree[rt>>1].w+=tree[rt].add*(tree[rt>>1].r-tree[rt>>1].l+1);
tree[rt>>1|1].w+=tree[rt].add*(tree[rt>>1|1].r-tree[rt>>1|1].l+1);
tree[k].add=0;
}
int quary_single(int rt,int a)
{
if(t[rt].l==t[rt].r)
{
return t[i].w;
}
int mid=(t[rt].l+t[rt].r)>>1;
if(a<=mid) quary_single(rt<<1,a);
else quary_single(rt<<1|1,a);
}
void updata_single(int rt,int a,int b)
{
if(t[rt].l==t[rt].r)
{
t[i].w=b;
return ;
}
int mid=(t[rt].l+t[rt].r)>>1;
if(a<=mid) updata_single(rt<<1,a,b);
else updata_single(rt<<1|1,a,b);
push_up(rt);
}
int query_interval(int rt,int l,int r,int x,int y)
{
if(x>r||y<l) return 0;
if(x<=l&&r<=y) return t[rt].w;
push_down(rt);
int mid=(l+r)>>1;
return query_interval(rt<<1,l,mid,x,y)+query_interval(rt<<1|1,mid+1,r,x,y);
}
void update_interval(int rt, int l, int r, int x, int y, int w)
{
if(x>r||y<l) return ;
if(x<=l&&r<=y)
{
t[rt].add+=w;
t[rt].w+=w;
return ;
}
push_down(rt);
int mid=(l+r)>>1;
update_interval(rt>>1,l,mid,x,y,w);
update_interval(rt>>1|1,mid+1,r,x,y,w);
push_up(rt);
}
int main()
{
int n,m,op,l,r,x,y;
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--)
{
scanf("%d",&op);
ans=0;
if(op==1)//单点查询,输出第x个数
{
scanf("%d",&x);
printf("%d",quary_single(1,x));
}
else if(op==2) //单点修改
{
scanf("%d%d",&x,&y);
updata_single(1,x,y);
}
else if(op==3)//区间查询
{
scanf("%d%d",&l,&r);
printf("%d\n",query_interval(1,n,l,r));
}
else
{
scanf("%d%d%d",&l,&r,&x);//区间修改
update_interval(1,1,n,l,r,x);
}
}
}
题目
上洛谷去找吧~~ (〃‘▽’〃)