线段树入门+例题

Posted skywalker767

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树入门+例题相关的知识,希望对你有一定的参考价值。

基本原理和操作

1.建树,递归建法:

  • build(int u , int l , int r)
  • pushup
    u : 当前父节点
    l,r,当前建树区间。

pushup函数一般由题意而定,作用是利用子节点更新父节点

void build(int u , int l , int r)
{
   // l == r ,到了叶子节点,那么直接构造return; 
   if(l == r)
   {
   	tr[u] = {l , r .....}; 
   	return ;
   }
   else
   {
   	//建造当前节点。 
   	tr[u] = {l , r};
   	//以mid分出子节点 
   	int mid = l + r >> 1;
   	//递归建造子树 
   	build(u << 1 , l , mid) , build(u << 1 | 1 , mid + 1 , r);
   	//一般需要pushup() , 如果是建造空树,那么此步可以省略。 
   	pushup(....);
   }
}

2.单点修改,区间查询

  • modify(int u , int x , int v)
  • query(int u , int l , int r)
    u , 当前父节点
    l , r 当前查询区间。
    x , 修改位置。
    v ,修改值。
// 以 AcWing 1275. 最大数 为例 
void modify(int u, int x, int v)
{
	//找到 x 位置。 
    if (tr[u].l == x && tr[u].r == x) tr[u].v = v;
    else
    {
    	//求当前父节点的中间值,判断接下来向哪边递归。 
        int mid = tr[u].l + tr[u].r >> 1;
        //如果找的点在 mid 左侧,递归到左儿子,否则去右儿子。 
        if (x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        //修改完,pushup。 
        pushup(u);
    }
}
int query(int u, int l, int r)
{
	// 树中节点,已经被完全包含在[l, r]中了
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].v;
	
	//求当前节点中间值,判断应该向哪个儿子递归答案。 
    int mid = tr[u].l + tr[u].r >> 1;
    int v = 0;
    if (l <= mid) v = query(u << 1, l, r);
    if (r > mid) v = max(v, query(u << 1 | 1, l, r));

    return v;
}

例题:

AcWing 1275. 最大数

#include <iostream>

using namespace std;

const int N = 2e5 + 10;

int m , p , n;
struct Node
{
    int l , r;
    int v;
}tr[N * 4];

void build(int u ,int l , int r)
{
    tr[u] = {l , r};
    if(l == r) return ;
    int mid = tr[u].l + tr[u].r >> 1;
    build(u << 1 , l , mid) , build(u << 1 | 1 , mid + 1 , r );
}

int query(int u , int l , int r)
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].v;
    
    int mid = tr[u].l + tr[u].r >> 1;
    int v = 0;
    if(l <= mid) v = query(u << 1 , l , r);
    if(r > mid) v = max(v , query(u << 1 | 1 , l , r));
    return v;
}

void modify(int u , int x , int v)
{
    if(tr[u].l == tr[u].r) tr[u].v = v;
    else 
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(x <= mid) modify(u << 1 , x , v);
        else  modify(u << 1 | 1 , x , v);
        tr[u].v = max(tr[u << 1].v , tr[u << 1 | 1].v);
    }
}

int main()
{
    n = 0 ;
    int last = 0;
    scanf("%d%d" , &m , &p);
    build(1 , 1 , m);
    while(m --)
    {
        char op[2];
        int t;
        scanf("%s%d",op , &t);
        if(*op == 'A') 
        {
            modify(1 , n + 1 , (t + last) % p);
            n ++;
        }
        else
        {
            last = query(1 ,n - t + 1  , n);
            printf("%d\\n" , last);
        }
    }
    return 0;
}

(栗题)AcWing 245. 你能回答这些问题吗

#include <iostream>

using namespace std;

const int N = 500010;

int w[N];
int n , m;
struct Tr
{
	int l , r;
	int lmax , rmax , tmax, sum;
	#define l(x) tr[x].l
	#define r(x) tr[x].r
	#define sum(x) tr[x].sum
	#define lmax(x) tr[x].lmax
	#define rmax(x) tr[x].rmax
	#define tmax(x) tr[x].tmax
}tr[N * 4];

void pushup(Tr u ,  Tr l , Tr r)
{
	u.sum = l.sum + r.sum;
	u.lmax = max(l.lmax , l.sum + r.lmax);
	u.rmax = max(r.rmax , r.sum + l.rmax);
	u.tmax = max(max(l.tmax , r.tmax) , l.rmax + r.lmax);
}

void pushup(int u)
{
	pushup(tr[u] , tr[u << 1] , tr[u << 1 | 1]);
}

void build(int u , int l , int r)
{
	if(l == r)
	{
		tr[u] = {l , r , w[r] , w[r] , w[r] , w[r]};
		return ;
	}
	else
	{
		tr[u] = {l , r};
		int mid = l + r >> 1;
		build(u << 1 , l , mid) , build(u << 1 | 1 , mid + 1 , r);
		pushup(u);
	}
}

void modify(int u , int x , int v)
{
	if(l(u) == x && r(u) == x) tr[u] = {x , x , v , v , v , v};
	else
	{
		int mid = l(u) + r(u) >> 1;
		if(x <= mid) modify(u << 1 , x , v);
		else modify(u << 1 | 1 , x  , v);
		pushup(u);
	}
}

Tr query(int u , int l , int r)
{
	if(l(u) >= l && r(u) <= r) return tr[u];
	else
	{
		int mid = l(u) + r(u) >> 1;
		if(r <= mid) return query(u << 1 , l , r);
		else if(l > mid) return query(u << 1 | 1 , l , r);
		else
		{
			auto left = query(u << 1 , l , r);
			auto right = query(u << 1 | 1 , l , r);
			Tr res;
			pushup(res , left , right);
			return res;
		}
	}
}

int main()
{
	scanf("%d%d" , &n , &m);
	for(int i = 0;i < n;i ++) scanf("%d" , &w[i]);
	build(1 , 1 , n);
	
	while(m --)
	{
		int op;
		if(op == 1)
		{
			int x , y;
			scanf("%d%d",&x, &y);
			if(x > y) swap(x , y);
			auto res = query(1 , x , y);
			printf("%d" , res.tmax);
		}
		else
		{
			int x , y;
			scanf("%d%d",&x, &y);
			modify(1 , x , y);
		}
	}
	return 0;
}

待更新

pushdown操作(lazy 标记)

可持久化线段树(主席树)

参考文献

AcWing - yxc
算法进阶指南
线段树 从入门到进阶(超清晰,简单易懂)

以上是关于线段树入门+例题的主要内容,如果未能解决你的问题,请参考以下文章

一般线段树与权值线段树

可持久化线段树入门浅谈

可持久化线段树入门浅谈

线段树模板总结

luogu P3372ybtoj线段树课堂过关例题2区间查改 &模板线段树 1

神奇的操作——线段树合并(例题: BZOJ2212)