线段树是一种超强的数据皆狗,支持区间查询,区间修改,单点修改,区间求和和其他变形问题。
线段树强大在于它平摊了查询和修改的时间复杂度。
线段树概念没啥好讲的,我们直接看模板题。
然后代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100001;
int a[maxn];
struct stree
{
int l;//p节点左右端点 (其实这个左右端点你存不存关系不大,因为你二分的左右端点就是l,r;
int r;
int data;//这个存最大值
}tree[4*maxn];//记住一定要开4倍
void build(int p,int l,int r)//造树
{
tree[p].l=l; tree[p].r=r;//存一下
if(l==r) //搜到一个点
{
tree[p].data=a[l]; //最大值是本身
return;
}
int mid=(l+r)/2; //二分递归下去
build(p*2,l,mid);
build(p*2+1,mid+1,r);
tree[p].data=max(tree[p*2].data,tree[p*2+1].data); //不要忘了在返回时合并左右端点信息;
}
void charge(int p,int x,int v) //单点修改 x为要修改的点,v为要修改成的值。
{
if(tree[p].l==tree[p].r) // 找到这个点了
{
tree[p].data=v; //修改他
return;
}
int mid=(tree[p].l+tree[p].r)/2;
if(x<=mid) //该点在左边,递归左子树;
charge(p*2,x,v);更改信息
else //同理
charge(p*2+1,x,v);
tree[p].data=max(tree[p*2].data,tree[p*2+1].data); //别忘了返回时更改信息
}
int ask(int p,int l,int r) //区间查询min
{
if(l==tree[p].l&&r==tree[p].r) //刚好搜到这个区间,return此区间max。
return tree[p].data;
int mid=(tree[p].l+tree[p].r)/2;
if(r<=mid) //询问区间在搜到区间左子树,递归左子树
return ask(p*2,l,r);
if(l>mid) //同理
return ask(p*2+1,l,r);
else //询问区间和左右子树都有重叠,那都搜吧。
return max(ask(p*2,l,mid),ask(p*2+1,mid+1,r));
}
int main()
{
int n;
int x,y,z;
cin>>n;
build(1,1,n);
for(int i=1;i<=n;i++)
{
cin>>x>>y>>z;
if(x==1)
charge(1,y,z);
else
cout<<ask(1,y,z)<<endl;
}
return 0;
}
cOlOc K!!!
以上是单点修改,和区间查询。
那我们再来一道模板题:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int ans=-10001;
int x,y,z;
struct QGws
{
int data,lzy; //注意这里的结构体和上一题不一样,其实没啥区别。
//区间修改并不会真正修改每一个值,但是会在修改的区间内设lzy标记,记录要修改了多少,当要查询是lzy标记会向下传递,作用在他影响的那些点上。(就像差分约束)
}t[4*maxn];
void pushdown(int x) //lzy标记向下传递
{
t[2*x].data+=t[x].lzy;
t[2*x].lzy+=t[x].lzy;
t[2*x+1].data+=t[x].lzy;
t[2*x+1].lzy+=t[x].lzy;
t[x].lzy=0; //传递完之后他自己lzy就变成0了。
}
void charge(int l,int r,int p) //区间修改 x,y是要修改的区间,l,r是当前搜到的区间。
{
if(r<x||l>y) return; //搜到的区间与要修改区间无重叠,剪枝剪掉。
if(x<=l&&y>=r) //搜到的区间完全包含在要修改区间内,修改该区间信息。
{
t[p].data++;
t[p].lzy++;
return;
}
pushdown(p); //传递他的lzy标记
int mid=(l+r)/2;
charge(l,mid,p*2);
charge(mid+1,r,p*2+1);
t[p].data=max(t[p*2].data,t[p*2+1].data); //日常在返回时更新信息
}
void sechi(int l,int r,int p) //虚伪的区间查询
{
if(r<x||l>y) return; //与上面同理
if(x<=l&&y>=r)
{
ans=max(ans,t[p].data); //完全包含,该区间的最值可作为最终答案的参考。
return;
}
pushdown(p);//别忘记传递
int mid=(l+r)/2;
sechi(l,mid,p*2);
sechi(mid+1,r,p*2+1);
// 无需更新了,因为该操作不会改变信息
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>z>>x>>y;
if(z==1)
charge(1,n,1);
else
{
ans=0;
sechi(1,n,1);
cout<<ans<<endl;
}
}
return 0;
两道模版题希望帮助大家更好理解线段树!