0.适用范围
线段树是一种数据结构,用于进行区间快速修改及查询。
1.先导知识
二叉树(略)
2.定义
线段树的每个结点代表着一个区间,我们将这棵树以数组的方式存储。
设一个下标为、代表区间的结点,二分该结点,则该结点的左孩子(假如有的话)下标为、代表区间;右孩子(假如有的话)下标为、代表区间
从线段树的定义中可以发现,需要开大约4倍空间(或者说是大于等于n的最小的2的正数次幂*2)
3.lazy标志
lazy标志表示该下标对于该lazy值已计入计算,但其子节点还未用这个lazy值进行更新
因此需要使用pushDown函数实现lazy标志的传递
void pushDown(int id)
{
if(segTree[id].lazy)
{
int lid=id<<1,rid=id<<1|1;
segTree[lid].lazy+=segTree[id].lazy;
segTree[rid].lazy+=segTree[id].lazy;
segTree[lid].val+=segTree[id].lazy*(segTree[lid].r-segTree[lid].l+1);
segTree[rid].val+=segTree[id].lazy*(segTree[rid].r-segTree[rid].l+1);
segTree[id].lazy=0;
}
}//向下传递lazy
4.建树
按照定义建树即可
void pushUp(int id)
{
segTree[id].val=segTree[id<<1].val+segTree[id<<1|1].val;
}//上推
void build(int l,int r,int id)
{
segTree[id].l=l;
segTree[id].r=r;
if(l==r)
{
//-------------------------------------------------
segTree[id].val=arr[l];
//该区间是点,赋值为该点上的值
//-------------------------------------------------
return;
}
int m=(l+r)>>1;
build(l,m,id<<1);
build(m+1,r,id<<1|1);
pushUp(id);
}//建树
5.更新
void apply(int id,ll add)
{
//-------------------------------------------------
//视题目情况做操作
segTree[id].val+=add*(segTree[id].r-segTree[id].l+1);
segTree[id].lazy+=add;
//此处为区间加
//-------------------------------------------------
}//更新时具体需要做的操作
void update(int l,int r,int id,ll add)
{
//-------------------------------------------------
if(r<segTree[id].l||l>segTree[id].r) return;
//该区间与要更新的区间不相交
//-------------------------------------------------
if(l<=segTree[id].l&&r>=segTree[id].r)
{
//-------------------------------------------------
apply(id,add);
return;
//该区间整个包含于要更新的区间
//-------------------------------------------------
}
pushDown(id);
update(l,r,id<<1,add);
update(l,r,id<<1|1,add);
pushUp(id);
}//更新
6.查询
ll query(int l,int r,int id)
{
if(r<segTree[id].l||l>segTree[id].r) return 0;
if(l<=segTree[id].l&&segTree[id].r<=r)
{
return segTree[id].val;
}
pushDown(id);
ll ans=0;
ans+=query(l,r,id<<1);
ans+=query(l,r,id<<1|1);
return ans;
}//查询
例题
区间加修改、区间和查询
#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef struct SegNode
{
int l,r;
ll lazy,val;
}segNode;
const int maxn=1e5;
ll arr[maxn+5];
segNode segTree[4*maxn+5];
void pushUp(int id)
{
segTree[id].val=segTree[id<<1].val+segTree[id<<1|1].val;
}//上推
void pushDown(int id)
{
if(segTree[id].lazy)
{
int lid=id<<1,rid=id<<1|1;
segTree[lid].lazy+=segTree[id].lazy;
segTree[rid].lazy+=segTree[id].lazy;
segTree[lid].val+=segTree[id].lazy*(segTree[lid].r-segTree[lid].l+1);
segTree[rid].val+=segTree[id].lazy*(segTree[rid].r-segTree[rid].l+1);
segTree[id].lazy=0;
}
}//向下传递lazy
void build(int l,int r,int id)
{
segTree[id].l=l;
segTree[id].r=r;
if(l==r)
{
//-------------------------------------------------
segTree[id].val=arr[l];
//该区间是点,赋值为该点上的值
//-------------------------------------------------
return;
}
int m=(l+r)>>1;
build(l,m,id<<1);
build(m+1,r,id<<1|1);
pushUp(id);
}//建树
void apply(int id,ll add)
{
//-------------------------------------------------
//视题目情况做操作
segTree[id].val+=add*(segTree[id].r-segTree[id].l+1);
segTree[id].lazy+=add;
//此处为区间加
//-------------------------------------------------
}//更新时具体需要做的操作
void update(int l,int r,int id,ll add)
{
//-------------------------------------------------
if(r<segTree[id].l||l>segTree[id].r) return;
//该区间与要更新的区间不相交
//-------------------------------------------------
if(l<=segTree[id].l&&r>=segTree[id].r)
{
//-------------------------------------------------
apply(id,add);
return;
//该区间整个包含于要更新的区间
//-------------------------------------------------
}
pushDown(id);
update(l,r,id<<1,add);
update(l,r,id<<1|1,add);
pushUp(id);
}//更新
ll query(int l,int r,int id)
{
if(r<segTree[id].l||l>segTree[id].r) return 0;
if(l<=segTree[id].l&&segTree[id].r<=r)
{
return segTree[id].val;
}
pushDown(id);
ll ans=0;
ans+=query(l,r,id<<1);
ans+=query(l,r,id<<1|1);
return ans;
}//查询
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
build(1,n,1);
while(m--)
{
int op,x,y;ll k;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%lld",&x,&y,&k);
update(x,y,1,k);
}
else
{
scanf("%d%d",&x,&y);
ll ans=query(x,y,1);
cout<<ans<<endl;
}
}
return 0;
}
2020CCPC绵阳站J题 Joy of Handcraft
调和级数趋近于log(类似于分块的思维)+线段树
#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef struct SegNode
{
int l,r,lazy,val;
}segNode;
const int maxn=1e5;
int maxx[maxn+5];
segNode segTree[4*maxn+5];
void pushUp(int id)
{
segTree[id].val=max(segTree[id<<1].val,segTree[id<<1|1].val);
}//上推
void pushDown(int id)
{
if(segTree[id].lazy)
{
int lid=id<<1,rid=id<<1|1;
segTree[lid].lazy=max(segTree[lid].lazy,segTree[id].lazy);
segTree[rid].lazy=max(segTree[rid].lazy,segTree[id].lazy);
segTree[lid].val=max(segTree[id].lazy,segTree[lid].val);
segTree[rid].val=max(segTree[id].lazy,segTree[rid].val);
segTree[id].lazy=0;
}
}//向下传递lazy
void build(int l,int r,int id)
{
segTree[id].l=l;
segTree[id].r=r;
if(l==r)
{
segTree[id].lazy=0;
segTree[id].val=0;
return;
}
int m=(l+r)>>1;
build(l,m,id<<1);
build(m+1,r,id<<1|1);
pushUp(id);
}//建树
void apply(int id,int add)
{
segTree[id].val=max(segTree[id].val,add);
segTree[id].lazy=max(segTree[id].lazy,add);
}//更新时具体需要做的操作
void update(int l,int r,int id,int add)
{
if(r<segTree[id].l||l>segTree[id].r) return;
if(l<=segTree[id].l&&r>=segTree[id].r)
{
apply(id,add);
return;
}
//pushDown(id);
update(l,r,id<<1,add);
update(l,r,id<<1|1,add);
pushUp(id);
}//更新
int query(int l,int r,int id)
{
if(r<segTree[id].l||l>segTree[id].r) return 0;
if(l<=segTree[id].l&&segTree[id].r<=r)
{
return segTree[id].val;
}
pushDown(id);
int ans=max(query(l,r,id<<1),query(l,r,id<<1|1));
return ans;
}//查询
int main()
{
int t;
scanf("%d",&t);
for(int tot=1;tot<=t;tot++)
{
memset(segTree,0,sizeof(segTree));
memset(maxx,0,sizeof(maxx));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int ti,xi;
scanf("%d%d",&ti,&xi);
maxx[min(ti,m)]=max(maxx[min(ti,m)],xi);
}
build(1,m,1);
for(int i=1;i<=m;i++)
{
int ti=i;
int xi=maxx[ti];
for(int j=1;j<=m;j+=2*ti)
{
update(j,j+ti-1,1,xi);
}
}
printf("Case #%d:",tot);
for(int i=1;i<=m;i++)
{
int ans=query(i,i,1);
printf(" %d",ans);
}
printf("\n");
}
return 0;
}