老板子
建树板子
void maketree(int c[], int tree[], int node, int start, int end) {
if (start == end)
tree[node] = c[start];
else {
int mid = (start + end) / 2;
int l_node = 2 * node + 1;
int r_node = 2 * node + 2;
maketree(c, tree, l_node, start, mid);
maketree(c, tree, r_node, mid+1, end);
tree[node] = tree[l_node] + tree[r_node];
}
}
更改值板子
void updatetree(int c[], int tree[], int node, int start, int end, int idx/*更改的位置*/, int val/*更改的值*/) {
if (start == end) {
//原数组要更改
c[idx] = val;
tree[node] = val;
}
else {
int mid = (start + end) / 2;
int l_node = 2 * node + 1;
int r_node = 2 * node + 2;
if (idx >= start && idx <= mid) {//更改左子树
updatetree(c, tree, l_node, start, mid, idx, val);
}
else {//右子树
updatetree(c, tree, r_node, mid, end, idx, val);
}
tree[node] = tree[l_node] + tree[r_node];
}
}
区间和板子
int sumtree(int c[], int tree[], int node, int start, int end, int L, int R) {
if (R<start || L>end) {//不在范围内
return 0;
}
if (L <= start && R >= end) {
return tree[node];
}
if (start == end) {
return tree[node];
}
else {
int mid = (start + end) / 2;
int l_node = node * 2 + 1;
int r_node = node * 2 + 2;
int l_sum = sumtree(c, tree, l_node, start, mid, L, R);
int r_sum = sumtree(c, tree, r_node, mid + 1, end, L, R);
return l_sum + r_sum;
}
}
新板子
上面是在B站一个大神的板子
但是他和“主流”的板子不大一样,我在看lazy标记的时候就很困难,所以我再列举一下常规的板子
先来一个结构体
struct www{
int l;
int r;
int v;
int add;//懒标记
}
递归建树
inline void build(int i,int l,int r){
tree[i].l=l;tree[i].r=r;
if(l==r){
tree[i].v=c[l];return ;
}
int mid=(l+r)>>1;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
tree[i].v=tree[i*2].v+tree[i*2+1].v;
}
懒标记
void update(int p){
if(t[p].add){//如果懒标记不为0,就将其下传,修改左右儿子维护的值
//更改左右儿子的v和add
t[p*2].v+=t[p].add*(t[p*2].r-t[p*2].l+1);
t[p*2+1].v+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
t[p*2].add+=t[p].add; t[p*2+1].add+=t[p].add;
t[p].add=0;//下传之后将该节点的懒标记清0
}
}
区间修改
void change(int p,int x,int y,int z){//x,y是要更改区间
if(x<=t[p].l && t[p].r<=y){//被覆盖的话,就对其进行修改
t[p].v+=z*(t[p].r-t[p].l+1);
t[p].add+=z;//打上懒标记,先不向下延展
return;
}
spread(p);
/*如果发现没有被覆盖,那就需要继续向下找,
考虑儿子所维护的区间可能因为懒标记的存在而没有修改,
因此将懒标记下放*/
int mid=t[p].l+t[p].r>>1;
if(x<=mid) change(p*2,x,y,z);//如果要修改的区间覆盖了左儿子,就修改左儿子
if(y>mid) change(p*2+1,x,y,z);//右儿子同理
t[p].v=t[p*2].v+t[p*2+1].v;//最终维护的值等于左儿子的值+右儿子的值
}
区间查询
long long ask(int p,int x,int y){
if(x<=t[p].l && y>=t[p].r) return t[p].v;//如果被覆盖,就返回维护的值
spread(p);//下传懒标记,并查询左右儿子
int mid=t[p].l+t[p].r>>1;
long long ans=0;
if(x<=mid) ans+=ask(p*2,x,y);
if(y>mid) ans+=ask(p*2+1,x,y);//累加答案,返回左右儿子的和
return ans;
}