[数据结构]线段树专辑

(以下内容多数出自notonlysuccess大神的线段树专辑


首先是准备知识( 好吧, 我承认我是弱菜

  • 满二叉树和完全二叉数中 非叶节点 id 的子节点序号分别为 id*2, id*2+1;(id<<1,id<<1|1)

在代码前先介绍一些我的线段树风格:

  • maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
  • lson和rson分辨表示结点的左儿子和右儿子,由于每次传参数的时候都固定是这几个变量,所以可以用预定于比较方便的表示
  • 以前的写法是另外开两个个数组记录每个结点所表示的区间,其实这个区间不必保存,一边算一边传下去就行,只需要写函数的时候多两个参数,结合lson和rson的预定义可以很方便
  • PushUP(int rt)是把当前结点的信息更新到父结点
  • PushDown(int rt)是把当前结点的信息更新给儿子结点
  • rt表示当前子树的根(root),也就是当前所在的结点
一 单点更新 (最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上
  • hdu1166 敌兵布阵
    题意:O(-1)
    思路:O(-1)
    线段树功能:update:单点增减 query:区间求和
  • #include <cstdio>
    
    #define lson l , m , rt << 1
    #define rson m + 1 , r , rt << 1 | 1
    /*上面真是一个超棒的风格呢*/
    const int maxn = 55555;
    int sum[maxn<<2];
    
    void PushUP(int rt)
    {
    	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    	/*位运算运用, 尽量学起来吧*/
    }
    void build(int l,int r,int rt) 
    {
    	if (l == r) 
    	{
    		scanf("%d",&sum[rt]);
    		/*原来输入是可以写在这里的啊*/
    		return ;
    	}
    	int m = (l + r) >> 1;
    	build(lson);
    	build(rson);
    	PushUP(rt);
    }
    void update(int p,int add,int l,int r,int rt) 
    {
    	if (l == r)
    	{
    		sum[rt] += add;
    		return ;
    	}
    	int m = (l + r) >> 1;
    	if (p <= m) 
    		update(p , add , lson);
    	else 
    		update(p , add , rson);
    	PushUP(rt);
    }
    int query(int L,int R,int l,int r,int rt) 
    {
    	/*查询函数要注意的是返回的方式*/
    	if (L <= l && r <= R) 
    	{
    		return sum[rt];
    	}
    	int m = (l + r) >> 1;
    	int ret = 0;
    	if (L <= m) 
    		ret += query(L , R , lson);
    	if (R > m) 
    		ret += query(L , R , rson);
    	return ret;
    }
    int main() 
    {
    	int T , n;
    	scanf("%d",&T);
    	for (int cas = 1 ; cas <= T ; cas ++)
    	{
    		printf("Case %d:\n",cas);
    		scanf("%d",&n);
    		build(1 , n , 1);
    		char op[10];
    		while (scanf("%s",op)) 
    		{
    			if (op[0] == 'E') break;
    			int a , b;
    			scanf("%d%d",&a,&b);
    			if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
    			else if (op[0] == 'S') update(a , -b , 1 , n , 1);
    			else update(a , b , 1 , n , 1);
    		}
    	}
    	return 0;
    }


  • 关于线段树的基本概念一般可以从这题出来。树的结构靠递归维持,也就是说不需要传统的链式节点建树。

  • hdu1754 I Hate It
    题意:O(-1)
    思路:O(-1)
    线段树功能:update:单点替换 query:区间最值
  • #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    #define lson l , m , rt << 1
    #define rson m + 1 , r , rt << 1 | 1
    const int maxn = 222222;
    int MAX[maxn<<2];
    void PushUP(int rt) 
    {
    	MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);
    }
    void build(int l,int r,int rt)
    {
    	if (l == r) 
    	{
    		scanf("%d",&MAX[rt]);
    		return ;
    	}
    	int m = (l + r) >> 1;
    	build(lson);
    	build(rson);
    	PushUP(rt);
    }
    void update(int p,int sc,int l,int r,int rt) 
    {
    	if (l == r) 
    	{
    		MAX[rt] = sc;
    		return ;
    	}
    	int m = (l + r) >> 1;
    	if (p <= m) 
    		update(p , sc , lson);
    	else 
    		update(p , sc , rson);
    	PushUP(rt);
    }
    int query(int L,int R,int l,int r,int rt) 
    {
    	if (L <= l && r <= R) 
    	{
    		return MAX[rt];
    	}
    	int m = (l + r) >> 1;
    	int ret = 0;
    	if (L <= m)
    		ret = max(ret , query(L , R , lson));
    	if (R > m) 
    		ret = max(ret , query(L , R , rson));
    	return ret;
    }
    int main() 
    {
    	int n , m;
    	while (~scanf("%d%d",&n,&m)) 
    	{
    		build(1 , n , 1);
    		while (m --) {
    			char op[2];
    			int a , b;
    			scanf("%s%d%d",op,&a,&b);
    			if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
    			else update(a , b , 1 , n , 1);
    		}
    	}
    	return 0;
    }
  • 与上一题的代码相似度极高。
  • 要注意的地方有:
  • 结构变量表示方式与更新方式(本题中即pushup())根据需求变化
  • 有时候甚至不需要建树, 或者说数组的初始状态已经是符合需求的初始状态!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值