数据皆狗之线段树(模板)

线段树是一种超强的数据皆狗,支持区间查询,区间修改,单点修改,区间求和和其他变形问题。
线段树强大在于它平摊了查询和修改的时间复杂度。
线段树概念没啥好讲的,我们直接看模板题。这里写图片描述
然后代码:

#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;

两道模版题希望帮助大家更好理解线段树!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值