未完。

线段树是一棵二叉树,可以用来维护区间加、区间乘、区间求和、甚至区间 gcd、区间最大子段和等操作(然而本蒟蒻并不会写

0.图

here

1. 存储

线段树可以用结构体来存,其中需要保存的有这个节点的左端点右端点区间和lazy tag

代码:

struct tree{
	int l,r,x,add;
};
tree tr[N<<2];

2.建树

递归建树:

有一个节点编号 pos,和左右端点 l,r

如果 l=r,将 trpos.x 赋为 al,返回。

否则求出 mid,继续递归建树。

最后将 trpos.x 赋为左、右儿子的区间和之和,返回。

代码:

void build(int pos,int l,int r){
	tr[pos].l=l;tr[pos].r=r;
	if (l==r){
		tr[pos].x=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(pos<<1,l,mid);
	build(pos<<1|1,mid+1,r);
	tr[pos].x=tr[pos<<1].x+tr[pos<<1|1].x;
	return;
}

3.查询

比修改简单。

对于每一个结点,如果它完全在查询的范围中,直接返回。

否则继续递归访问它的左、右儿子。

代码:

int inq(int pos,int l,int r){
	if (tr[pos].r<l||tr[pos].l>r) return 0;
	if (l<=tr[pos].l && tr[pos].r<=r) return tr[pos].x;
	return inq(pos<<1,l,r)+inq(pos<<1|1,l,r);
}

4.修改

这里需要用到一个叫 lazy tag (懒标记)的东西。

如果这个区间被完全覆盖,直接修改即可。

否则放下懒标记,再判断左、右儿子是否有部分在区间里,如果有,就进行递归修改。

最后这个结点的值就是新的左、右儿子的区间和之和。

代码:传懒标记 + 修改(区间加):

void tran(int pos){
	if (tr[pos].add){
		tr[pos<<1].x+=tr[pos].add*(tr[pos<<1].r-tr[pos<<1].l+1);
		tr[pos<<1|1].x+=tr[pos].add*(tr[pos<<1|1].r-tr[pos<<1|1].l+1);
		tr[pos<<1].add+=tr[pos].add;
		tr[pos<<1|1].add+=tr[pos].add;
		tr[pos].add=0;
	}
}
void inc(int pos,int l,int r,int x){
	if (l<=tr[pos].l&&tr[pos].r<=r){
		tr[pos].x+=x*(tr[pos].r-tr[pos].l+1);
		tr[pos].add+=x;
		return;
	}
	tran(pos);
	int mid=(tr[pos].l+tr[pos].r)>>1;
	if (l<=mid) inc(pos<<1,l,r,x);
	if (r>mid)  inc(pos<<1|1,l,r,x);
	tr[pos].x=tr[pos<<1].x+tr[pos<<1|1].x;
}

大概就这样吧。。。懒得更新了。。。