• [笔记]线段树学习笔记


    [笔记]线段树学习总结

    概念

    每个节点以结构体的方式存储,结构体包含以下几个信息:
    区间左端点、右端点;(这两者必有)
    这个区间要维护的信息(事实际情况而定,数目不等)。
    图例:
    Alt text

    性质

    1、每个节点的左孩子区间范围为[l,mid],右孩子为[mid+1,r]

    2、对于结点k,左孩子结点为2k,右孩子为2k+1,这符合完全二叉树的性质

    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    建树

    基本结构:

    a.对于二分到的每一个结点,给它的左右端点确定范围。

    b.如果是叶子节点,存储要维护的信息。

    c.累加。

    参考代码

    struct node{
    	int l,r,w;
    }tree[100010];
    inline void Build(int l,int r,int k){
    	tree[k].l = l,tree[k].r = r;
    	if(l == r){
    		scanf("%d",&tree[k].w);
    		return;
    	}
    	int mid = (tree[k].l + tree[k].r) / 2;
    	Build(l,mid,k * 2);//递归
    	Build(mid + 1,r,k * 2 + 1);//递归
    	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;//累加
    	return;
    }
    

    注意事项

    1.开结构体数组时要开4倍

    2.在输入完叶子结点后要return,因为叶子结点不用再递归了。

    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    单点查询

    基本结构:

    与二分查询法基本一致,如果当前枚举的点左右端点相等,即叶子节点,就是目标节点。否则设查询位置为x,当前结点区间范围为l,r,中点为mid。则如果x<=mid,就递归它的左孩子,否则递归它的右孩子

    参考代码

    inline int Ask_p(int x){
    	if(tree[x].l == tree[x].r){//叶子结点
    		return tree[x].w;
    	}
    	int mid = (tree[x].l + tree[x].r) / 2;
    	if(mid >= x)
    		Ask_p(x * 2);//目标位置比中点靠左,就递归左孩子 
    	else Ask_p(x * 2 + 1);//递归右孩子
    }
    

    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    单点修改

    基本结构:

    用单点查询的方法找到目标节点并修改它的权值

    参考代码

    inline void change(int x,int y,int k){
    	if(tree[k].l == tree[k].r){//找到目标节点
    		tree[k].w = y;//xiu'gai'quan'zhi
    		return;
    	}
    	int mid = (tree[k].l + tree[k].r) / 2;
    	if(mid >= x)
    		change(x,y,k * 2);
    	else change(x,y,k * 2 + 1);
    	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
    	return;
    } 
    

    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    区间查询

    基本结构:

    分情况来讨论:

    假设l,r为节点区间,x,y为目标区间,则有一下三种情况:


    参考代码

    inline int Ask_section(int x,int y,int k){
    	if(tree[k].l > y || tree[k].r < x)return 0; 
    	if(tree[k].l >= x && tree[k].r <= y){
    		return tree[k].w;
    	}
    	int mid = (tree[k].l + tree[k].r) / 2;
    	return Ask_section(x,y,k * 2) + Ask_section(x,y,k * 2 + 1); 
    }
    

    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    完整代码在此(乘法和加法)

    #include <bits/stdc++.h>
    using namespace std;
    struct node{
    	long long w,l,r;
    	long long tag_add = 0,tag_mul = 1;
    }tree[600010];    //4 times;
    long long ans,n,m,p;
    void build(long long l,long long r,long long num){
    	tree[num].l = l;tree[num].r = r;
    	tree[num].tag_mul = 1;
    	if(tree[num].l == tree[num].r){
    		cin>>tree[num].w;
    		tree[num].w %= p;
    		return;
    	}
    	long long mid = (tree[num].l + tree[num].r) / 2;
    	build(l,mid,num * 2);
    	build(mid + 1,r,num * 2 + 1);
    	tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
    	return;
    }
    void pushdown(long long num){
    	tree[num << 1].w = (tree[num << 1].w * tree[num].tag_mul + tree[num].tag_add * (tree[num << 1].r - tree[num << 1].l + 1)) % p;
    	tree[num << 1 | 1].w = (tree[num << 1 | 1].w * tree[num].tag_mul + tree[num].tag_add * (tree[num << 1 | 1].r - tree[num << 1 | 1].l + 1)) % p;
    	tree[num << 1].tag_mul *= tree[num].tag_mul;
    	tree[num << 1].tag_mul %= p;
    	tree[num << 1 | 1].tag_mul *= tree[num].tag_mul;
    	tree[num << 1 | 1].tag_mul %= p;
    	tree[num << 1].tag_add = (tree[num << 1].tag_add * tree[num].tag_mul + tree[num].tag_add) % p;
    	tree[num << 1 | 1].tag_add = (tree[num << 1 | 1].tag_add * tree[num].tag_mul + tree[num].tag_add) % p;
    	tree[num].tag_mul = 1;
    	tree[num].tag_add = 0;
    	return;
    }
    void ask_a(long long num,long long tar_l,long long tar_r){
    	if(tree[num].l >= tar_l && tree[num].r <= tar_r){
    		ans += tree[num].w;
    		ans %= p;
    		return;
    	}
    	pushdown(num);
    	long long mid = (tree[num].l + tree[num].r) / 2;
    	if(mid >= tar_l)	
    		ask_a(num * 2,tar_l,tar_r);
    	if(mid < tar_r)
    		ask_a(num * 2 + 1,tar_l,tar_r);
    }
    void add_a_mul(long long num,long long tar_l,long long tar_r,long long w){
    	if(tree[num].l >= tar_l && tree[num].r <= tar_r){
    		tree[num].tag_mul *= w;
    		tree[num].tag_mul %= p;
    		tree[num].w *= w;
    		tree[num].w %= p;
    		tree[num].tag_add *= w;
    		tree[num].tag_add %= p;
    		return;
    	}
    	pushdown(num);
    	long long mid = (tree[num].l + tree[num].r) / 2;
    	if(mid >= tar_l)
    		add_a_mul(num * 2,tar_l,tar_r,w);
    	if(mid < tar_r)
    		add_a_mul(num * 2 + 1,tar_l,tar_r,w);
    	tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
    }
    void add_a_add(long long num,long long tar_l,long long tar_r,long long w){
    	if(tree[num].l >= tar_l && tree[num].r <= tar_r){
    		tree[num].w += (tree[num].r - tree[num].l + 1) * w;
    		tree[num].w %= p;
    		tree[num].tag_add += w;
    		tree[num].tag_add %= p;
    		return;
    	}
    	pushdown(num);
    	long long mid = (tree[num].l + tree[num].r) / 2;
    	if(mid >= tar_l)
    		add_a_add(num * 2,tar_l,tar_r,w);
    	if(mid < tar_r)
    		add_a_add(num * 2 + 1,tar_l,tar_r,w);
    	tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
    }
    int main(){
    	cin>>n>>m>>p;
    	build(1,n,1);
    	for(int i = 1;i <= m;i++){
    		int opt;
    		cin>>opt;
    		if(opt == 1){
    			int x,y,w;
    			cin>>x>>y>>w;
    			add_a_mul(1,x,y,w);
    		}
    		if(opt == 2){
    			int x,y,w;
    			cin>>x>>y>>w;
    			add_a_add(1,x,y,w);
    		}
    		if(opt == 3){
    			int x,y;
    			cin>>x>>y;
    			ans = 0;
    			ask_a(1,x,y);
    			ans %= p;
    			cout<<ans<<endl;
    		}
    	}
    	return 0;
    }
    

    小结

    线段树真是个好东西

    参考来源

    未完待续·········

  • 相关阅读:
    wpf如何将图片设置为窗体的背景
    C#用Oracle.DataAccess中连接Oracle要注意版本问题!
    C#格式化数值结果表
    将字符串的16进制转换成byte[]
    Java各个类型与byte[]的转换
    记录下 Jquery的使用
    页面div与顶部有缝隙问题
    Js 转换Json返回的时间格式(转)
    Div垂直居中水平居中
    C# 日期格式大全
  • 原文地址:https://www.cnblogs.com/czy--blog/p/13543472.html
Copyright © 2020-2023  润新知