zoukankan      html  css  js  c++  java
  • 线段树

    第一版

    关于线段树本身

    1. 区间查询,单点修改
    2. 单点查询,区间修改
    3. lazy思想

    关于例题

    1. 有一个LG的模板

    我在恍惚之间又想起了那件事,仿佛就在昨天。

    学校,午饭后,两个人在校园里闲逛,迎面遇上yjx,说:"你们是两个人,真好。我谔谔。

    多说无益,从此,便是陌路人。总是无中生有,又有什么意思。

    正文

    原题链接

    我们先来了解一下线段树

    定义

    度娘上是这么说的 : 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

    限制:此处以询问区间和为例。实际上线段树可以处理很多符合结合律的操作。(比如说加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[2])+(a[3]+a[4]))

    线段树之所以称为“树”,是因为其具有树的结构特性。

    就以一个图片为例,你建出来的线段树大概是这样的

    表示

    对于每一个子节点而言,都表示整个序列中的一段子区间;对于每个叶子节点而言,都表示序列中的单个元素信息;子节点不断向自己的父亲节点传递信息,而父节点存储的信息则是他的每一个子节点信息的整合。

    我们来一波逐步肢解线段树

    线段树的实现

    节点表示与儿子节点

    typedef long long ll;
    #define fake ll
    struct TreeNode{
    	fake mul_lazy,add_lazy,sum,l,r;
        //l表示区间左端点,同理有r,mul_lazy表示乘的懒惰标记,同理有add_lazy,sum表示区间和
    }tree[Maxn*4];
    fake ls(fake k){return k<<1;}
    fake rs(fake k){return k<<1|1;}
    

    在线段树里,实际空间只能用到2*Maxn,但是为了避免越界,一般来说开到4*Maxn

    这里用位运算会快的。ls表示左节点,rs表示右节点

    维护

    push_up_sum()就是一个把儿子的信息传递给父亲

    void push_up_sum(fake k){tree[k].sum=tree[ls(k)].sum+tree[rs(k)].sum;}
    

    建树

    build函数

    那么对于建树,由于二叉树自身的父子节点之间的可传递关系,所以可以考虑递归建树,并且在建树的同时,我们应该维护父子节点的关系:

    void build(fake k,fake l,fake r){
    	tree[k].l=l,tree[k].r=r;tree[k].mul_lazy=1;
    	if(l==r){tree[k].sum=a[l];return ;}
    	fake mid=(l+r)>>1;
    	build(ls(k),l,mid);
    	build(rs(k),mid+1,r);
    	push_up_sum(k); 
    }
    

    区间修改

    chS_addchS_mul

    先来谈谈懒惰标记

    首先,懒标记的作用是记录每次、每个节点要更新的值,但线段树的优点不在于全记录(全记录依然很慢qwq),而在于传递式记录:

    可以自己画图意会

    void f(fake k,fake add,fake mul){
    	tree[k].sum=((tree[k].sum*mul)+(tree[k].r-tree[k].l+1)*add);
    	tree[k].mul_lazy=tree[k].mul_lazy*mul;
    	tree[k].add_lazy=(tree[k].add_lazy*mul+add);//更新一个点的懒惰标记 
    }
    void push_down(fake k){
    	f(ls(k),tree[k].add_lazy,tree[k].mul_lazy);
    	f(rs(k),tree[k].add_lazy,tree[k].mul_lazy);
    	tree[k].add_lazy=0;tree[k].mul_lazy=1;
    }
    

    就是说你把一个节点的懒惰标记传递给儿子的操作,自己想想操作顺序

    注意 懒惰标记是什么和本身无关,只关于传给节点的信息

    于是按照push_up_sum的逆向思想,可以查找此区间

    void chS_add(fake k,fake l,fake r,fake x){
    	if(l<=tree[k].l&&tree[k].r<=r){
    		tree[k].add_lazy=(tree[k].add_lazy+x);
    		tree[k].sum=(tree[k].sum+(tree[k].r-tree[k].l+1)*x);
    		return ;
    	}
    	push_down(k);
    	fake mid=(tree[k].l+tree[k].r)>>1;
    	if(l<=mid) chS_add(ls(k),l,r,x);
    	if(r>mid) chS_add(rs(k),l,r,x);
    	push_up_sum(k);
    	return ;
    }
    void chS_mul(fake k,fake l,fake r,fake x){
    	if(l<=tree[k].l&&tree[k].r<=r){
    		tree[k].mul_lazy=(tree[k].mul_lazy*x);
    		tree[k].add_lazy=(tree[k].add_lazy*x);
    		tree[k].sum=(tree[k].sum*x);
    		return ;
    	}
    	push_down(k);
    	fake mid=(tree[k].l+tree[k].r)>>1;
    	if(l<=mid) chS_mul(ls(k),l,r,x);
    	if(r>mid) chS_mul(rs(k),l,r,x);
    	push_up_sum(k);
    	return ; 
    }
    

    区间查询

    我们也用chS的思想,可以简单解决

    fake query(fake k,fake l,fake r){
    	if(l<=tree[k].l&&tree[k].r<=r){return tree[k].sum;}
    	fake mid=(tree[k].l+tree[k].r)>>1;;
    	fake ret=0;
    	push_down(k);
    	if(l<=mid) ret+=query(ls(k),l,r);
    	if(r>mid) ret+=query(rs(k),l,r);
    	return ret;
    }
    

    AC代码(线段树2)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <fstream>
    #include <stdio.h>
    #include <math.h>
    #include <stack>
    #include <map>
    #include <queue>
    #include <vector>
    #include <deque>
    #include <list>
    #include <set>
    #include <bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    #define fake ll
    #define size 666 
    using namespace std;
    fake read(){
    	fake x=0;
    	char ch=getchar();
    	while(ch<'0'||ch>'9') ch=getchar();
    	while(ch<='9'&&ch>='0'){
    		x=(x<<1)+(x<<3)+(ch-'0');
    		ch=getchar();
    	}
    	return x;
    }
    const int Maxn=1e5+11;
    struct TreeNode{
    	fake mul_lazy,add_lazy,sum,l,r;
    }tree[Maxn*4];
    fake a[Maxn],n,m,p,b,x,y;
    fake ls(fake k){return k<<1;}
    fake rs(fake k){return k<<1|1;}
    void build(fake k,fake l,fake r);
    void push_up_sum(fake k);//献上和 
    void push_down(fake k);//向下推进懒惰标记
    void f(fake k,fake add,fake mul);
    void chS_mul(fake k,fake l,fake r,fake x);
    void chS_add(fake k,fake l,fake r,fake x);
    fake query(fake k,fake l,fake r);
    void f(fake k,fake add,fake mul){
    	tree[k].sum=((tree[k].sum*mul)+(tree[k].r-tree[k].l+1)*add)%p;
    	tree[k].mul_lazy=tree[k].mul_lazy*mul%p;
    	tree[k].add_lazy=(tree[k].add_lazy*mul+add)%p;//更新一个点的懒惰标记 
    }
    void push_up_sum(fake k){tree[k].sum=(tree[ls(k)].sum+tree[rs(k)].sum)%p;}
    void build(fake k,fake l,fake r){
    	tree[k].l=l,tree[k].r=r;tree[k].mul_lazy=1;
    	if(l==r){tree[k].sum=a[l]%p;return ;}
    	fake mid=(l+r)>>1;
    	build(ls(k),l,mid);
    	build(rs(k),mid+1,r);
    	push_up_sum(k); 
    }
    void push_down(fake k){
    	f(ls(k),tree[k].add_lazy,tree[k].mul_lazy);
    	f(rs(k),tree[k].add_lazy,tree[k].mul_lazy);
    	tree[k].add_lazy=0;tree[k].mul_lazy=1;
    }
    void chS_add(fake k,fake l,fake r,fake x){
    	if(l<=tree[k].l&&tree[k].r<=r){
    		tree[k].add_lazy=(tree[k].add_lazy+x)%p;
    		tree[k].sum=(tree[k].sum+(tree[k].r-tree[k].l+1)*x)%p;
    		return ;
    	}
    	push_down(k);
    	fake mid=(tree[k].l+tree[k].r)>>1;
    	if(l<=mid) chS_add(ls(k),l,r,x);
    	if(r>mid) chS_add(rs(k),l,r,x);
    	push_up_sum(k);
    	return ;
    }
    void chS_mul(fake k,fake l,fake r,fake x){
    	if(l<=tree[k].l&&tree[k].r<=r){
    		tree[k].mul_lazy=(tree[k].mul_lazy*x)%p;
    		tree[k].add_lazy=(tree[k].add_lazy*x)%p;
    		tree[k].sum=(tree[k].sum*x)%p;
    		return ;
    	}
    	push_down(k);
    	fake mid=(tree[k].l+tree[k].r)>>1;
    	if(l<=mid) chS_mul(ls(k),l,r,x);
    	if(r>mid) chS_mul(rs(k),l,r,x);
    	push_up_sum(k);
    	return ; 
    }
    fake query(fake k,fake l,fake r){
    	if(l<=tree[k].l&&tree[k].r<=r){return tree[k].sum%p;}
    	fake mid=(tree[k].l+tree[k].r)>>1;;
    	fake ret=0;
    	push_down(k);
    	if(l<=mid) ret+=query(ls(k),l,r);
    	if(r>mid) ret+=query(rs(k),l,r);
    	return ret%p;
    }
    int main(){
    	freopen("Interval Tree.in","r",stdin);
    	n=read();m=read();p=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	build(1,1,n);
    	for(int i=1;i<=m;i++){
    		b=read();x=read();y=read();
    		if(b==1) chS_mul(1,x,y,read()); 
    		else if(b==2) chS_add(1,x,y,read());
    		else if(b==3) printf("%lld
    ",query(1,x,y));
    	}
    	return 0;
    } 
    

    愿线段树陪你出走半生,归来仍是少年。

  • 相关阅读:
    记一次把聊天表情包转成文件再还原的故事
    apue第九章之孤儿进程组
    Anatomy of a Database System学习笔记
    Anatomy of a Database System学习笔记
    Anatomy of a Database System学习笔记
    Anatomy of a Database System学习笔记
    Caffe学习中的一些错误记录
    接口自动化测试
    接口测试
    HTTP 协议相关
  • 原文地址:https://www.cnblogs.com/zhltao/p/12303807.html
Copyright © 2011-2022 走看看