线段树(nb)

今天刚学习了线段树,赶紧趁热打了两遍模版

下面都是线段树的基本操作,这个板子是维护的区间中的最大值,当然修改change和build包括线段树中的data可以维护区间上的不同信息。

首先介绍一下线段树这种数据结构吧

线段树是一种基于分治思想的二叉树结构,用于在区间上进行信息的统计与维护

板子的一些默认信息

1、线段树的每个结点都代表一个区间。
2、线段树具有唯一的跟结点,代表的区间是整个统计范围,即[1~N]。
3、线段树的每个叶子结点都代表一个长度为1的元区间[x, x]。
4、 对于每个内部节点[l, r],它的左子结点是[l, mid], 右子结点是[mid + 1, r]

#include <iostream>
using namespace std;

//线段树是基于分治的思想实现的

//struct数组存储线段树
struct SegmentTree  
{
    int l, r;
    int data;
}t[100 * 4];
int a[100];

//用线段树维护每个区间的最大值
void build(int p, int l, int r)
{
    t[p].l = l, t[p].r = r;
    if(r == l)  {t[p].data = a[l]; return ;}
    int mid = (l + r) >> 1;
    build(p*2, l, mid);
    build(p*2 + 1, mid + 1, r);
    t[p].data = max(t[p * 2].data, t[p*2 + 1].data);
}

//调用入口
// build(1, 1, n);


//线段树的单点修改
void change(int p, int x, int v)
{
    if(t[p].l == t[p].r)    {t[p].data = v; return ;}//找到要修改的叶子结点
    int mid = (t[p].l + t[p].r) >> 1;
    if(x <= mid)    change(p * 2, x ,v); //x属于左半区间
    else    change(p * 2 + 1, x, v); // 行属于右半区间
    t[p].data = max(t[p * 2].data, t[p * 2 + 1].data);//从下往上更新信息
}

//线段树的区间查询
int ask(int p, int l, int r)
{
    if(l <= t[p].l && r >= t[p].r)  return t[p].data;
    int mid = (t[p]. l + t[p].r) >> 1;
    int val = -(1 << 30); //初始化为负无穷大
    if(l <= mid)    val = max(val, ask(p * 2, l, r));
    if(r > mid)    val = max(val, ask(p * 2 + 1, l ,r));
    return val;
}
//调用出口 cout << ask(1, 1, r) << endl;

int main()
{
    int n;cin >> n;
    for(int i = 1; i <= n; ++ i)    cin >> a[i];
    build(1, 1, n);
    
    cout << ask(1, 2, 3);
}

若要维护区间最大连续子段和


#include <iostream>
using namespace std;

//线段树是基于分治的思想实现的

//struct数组存储线段树
struct SegmentTree  
{
    int l, r;
    int lmax, rmax, sum, data;
}t[100 * 4];
int a[100];

//用线段树维护每个区间的信息
void build(int p, int l, int r)
{
    t[p].l = l, t[p].r = r;
    if(r == l)  
    {
    	t[p].sum = a[l]; 
        t[p].lmax = a[l];
        t[p].rmax = a[l];
        t[p].data = a[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(p*2, l, mid);
    build(p*2 + 1, mid + 1, r);
    t[p].lmax = max(t[p * 2].lmax, t[p * 2].sum + t[p * 2 + 1].lmax);
    t[p].rmax = max(t[p * 2 + 1].rmax, t[p * 2 + 1].sum + t[p * 2].rmax);
    t[p].data = max(t[p * 2].data, t[p * 2 + 1].data);
    t[p].data = max(t[p].data,  t[p * 2].rmax + t[p * 2 + 1].lmax);
}
//调用入口
// build(1, 1, n);


//线段树的单点修改
void change(int p, int x, int v)
{
    if(t[p].l == t[p].r)    {t[p].data = v; return ;}//找到要修改的叶子结点
    int mid = (t[p].l + t[p].r) >> 1;
    if(x <= mid)    change(p * 2, x ,v); //x属于左半区间
    else    change(p * 2 + 1, x, v); // 行属于右半区间
    //从下往上更新信息
    t[p].sum = t[p * 2].sum + t[p * 2 + 1].sum;
    t[p].lmax = max(t[p * 2].lmax, t[p * 2].sum + t[p * 2 + 1].lmax);
    t[p].rmax = max(t[p * 2 + 1].rmax, t[p * 2 + 1].sum + t[p * 2].rmax);
    t[p].data = max(t[p * 2].data, t[p * 2 + 1].data);
    t[p].data = max(t[p].data,  t[p * 2].rmax + t[p * 2 + 1].lmax);
}

//线段树的区间查询
int ask(int p, int l, int r)
{
    if(l <= t[p].l && r >= t[p].r)  return t[p].data;
    int mid = (t[p]. l + t[p].r) >> 1;
    int val = -(1 << 30); //初始化为负无穷大
    if(l <= mid)    val = max(val, ask(p * 2, l, r));
    if(r > mid)    val = max(val, ask(p * 2 + 1, l ,r));
    return val;
}
//调用出口 cout << ask(1, 1, r) << endl;

int main()
{
    int n;cin >> n;
    for(int i = 1; i <= n; ++ i)    cin >> a[i];
    build(1, 1, n);
    
    cout << ask(1, 1, 5);
}

可以看出我们只需要修改build和change部分即可,只需要加入我们想要维护区间的额外信息。

线段树也太好用了吧QWQ!!!

posted @ 2022-10-27 14:57  cxy8  阅读(36)  评论(0编辑  收藏  举报