线段树是入门选手向正式选手过渡的重要算法工具
相比于树状数组,线段树虽写起来麻烦,但是连续区间修改和查询的功能非常强大。
像求区间之和,区间最大值,区间最大公因数等。
其延迟标记更是其强大所在,因为在区间更新时,一点一点更新的时间复杂度时log(n),而利用延迟标记下推可以大大减少时间复杂度!
模板:(杭电,张煊的金箍棒2)
#include<bits/stdc++.h>
using namespace std;
//树状数组中,子节点的下标是父节点的2倍和2倍+1
struct node{
int l,r,len,lazy,val;
}tree[400010];//树状数组的大小要是数据量的4倍
void buil(int l,int r,int root){//构造线段树
tree[root].l=l;
tree[root].r=r;
tree[root].lazy=0;
tree[root].len=r-l+1;//根据需要可添加其它元素
if(l==r){
tree[root].val=1;
}
else{
int m=(l+r)/2;
buil(l,m,root*2);
buil(m+1,r,root*2+1);
tree[root].val=tree[root*2].val+tree[root*2+1].val;
}
}
void pushdown(int root){//下推
if(tree[root].lazy){
tree[root*2].lazy=tree[root].lazy;
tree[root*2+1].lazy=tree[root].lazy;
tree[root*2].val=tree[root].lazy*tree[root*2].len; //根据需要变更,
tree[root*2+1].val=tree[root].lazy*tree[root*2+1].len; //是累加+=还是赋值=
tree[root].lazy=0;
}
}
void updata(int l,int r,int root,int x){//区间更新
if(l<=tree[root].l&&tree[root].r<=r){
tree[root].val=x*tree[root].len;//根据需要变更,是累加+=还是赋值=
tree[root].lazy=x;
return;
}
if(r<tree[root].l||tree[root].r<l) return;
pushdown(root);
updata(l,r,root*2,x);
updata(l,r,root*2+1,x);
tree[root].val=tree[root*2].val+tree[root*2+1].val;
}
int query(int l,int r,int root){//区间查询,注意返回类型
if(l<=tree[root].l&&tree[root].r<=r)return tree[root].val;
if(r<tree[root].l||tree[root].r<l)return 0;
pushdown(root);
return query(l,r,root*2)+query(l,r,root*2+1);
}
int main(){
ios::sync_with_stdio(0);
int T,n,m;
cin>>T;
while(T--){
cin>>n>>m;
buil(1,n,1);
for(int i=1;i<=m;i++){
int l,r,x;
cin>>l>>r>>x;
updata(l,r,1,x);
//for(int i=1;i<=40;i++)cout<<tree[i].val<<' ';cout<<endl;
}
cout<<query(1,10,1)<<endl;
}
return 0;
}