• 线段树1


    先讲解一下线段树

    线段树用于区间操作的优化,来看一道这样的题:

    https://www.luogu.org/problem/P2068

    在一个序列中,支持以下操作:修改序列中一个数,查询其中y一段区间的和

    元素数<=100000,操作数<=10000.

    由于数据较大,n*m显然过不了,这时,就要用到线段树了:

    这就是线段树示意图

    每个节点上都有一个区间,这个节点的点权就是这个区间所有数的和

    但不用担心初始化的时间,因为每个节点的点权等于它的左右儿子点权之和

    一个节点的左右儿子的点权分别对应l~(l+r)/2的和与(l+r)/2+1~r的和

    到叶节点时这个点权就是对应这个数。

    所以build就可以这样写

    void build(int l,int r,int root)
    {
    	if(l==r) 
    	{
    		num[root]=a[l];
    		return;
    	}
    	int mid=(l+r)/2;
    	build(l,mid,root*2);
    	build(mid+1,r,root*2+1);
    	num[root]=min(num[root*2],num[root*2+1]);
    }

    比如,我要查询的是3~7这个区间,那它的和就是

    这三个节点的点权之和

    现在我们看一下查询的时间复杂度,很明显大约是O(logn)

    现在,来看一下查询的代码

    int search(int p,int q,int l,int r,int root)
    {
    	if(p==l && r==q)
    	{
    		return num[root];
    	}
    	int mid=(l+r)/2;
    	if(p>=mid+1) return search(p,q,mid+1,r,root*2+1);
    	else if(q<=mid) return search(p,q,l,mid,root*2);
    	else return search(p,mid,l,mid,root*2)+search(mid+1,q,mid+1,r,root*2+1);
    }

    代码解释

    如果要查询的区间被该节点的左子树或右子树完全包含,就只用查找一个子树就行了

    否则要查询的区间一定是跨越了两个子树,这样就要查询两次

    如果要查询的区间与该节点对应的区间刚好重合,这样就直接return这个值就好了

    最后是修改,例如我要修改6这个节点,那要修改的就是

    这五个节点,时间复杂度也是O(logn)(因为每层一个)

    简单看一下代码

    void change(int p,int val,int l,int r,int root)
    {
    	if(l==r)
    	{
    		num[root]+=val;
    		return;
    	}
    	int mid=(l+r)/2;
    	if(p>=mid+1) change(p,val,mid+1,r,root*2+1);
    	else change(p,val,l,mid,root*2);
    	num[root]=num[root*2]+num[root*2+1];
    }
    

    代码解释

    如果要查询的节点再该节点的左子树,就查找一个左子树就行了

    否则就查找一个右子树就好了

    如果要查询的节点与该节点刚好重合,这样就直接修改这个值就好了

    最后是这道题的完整代码

    #include<bits/stdc++.h>
    using namespace std;
    int num[100000*4];
    void build(int l,int r,int root)
    {
    	if(l==r)
    	{
    		num[root]=0;
    		return;
    	}
    	int mid=(l+r)/2;
    	build(l,mid,root*2);
    	build(mid+1,r,root*2+1);
    	num[root]=num[root*2]+num[root*2+1];
    }
    void change(int p,int val,int l,int r,int root)
    {
    	if(l==r)
    	{
    		num[root]+=val;
    		return;
    	}
    	int mid=(l+r)/2;
    	if(p>=mid+1) change(p,val,mid+1,r,root*2+1);
    	else change(p,val,l,mid,root*2);
    	num[root]=num[root*2]+num[root*2+1];
    }
    int search(int p,int q,int l,int r,int root)
    {
    	//printf("%d %d %d %d %d
    ",p,q,l,r,root);
    	if(p==l && r==q)
    	{
    		return num[root];
    	}
    	int mid=(l+r)/2;
    	if(p>=mid+1) return search(p,q,mid+1,r,root*2+1);
    	else if(q<=mid) return search(p,q,l,mid,root*2);
    	else return search(p,mid,l,mid,root*2)+search(mid+1,q,mid+1,r,root*2+1);
    }
    int main()
    {
    	int n,w;
    	scanf("%d%d",&n,&w);
    	for(int i=1;i<=w;i++)
    	{
    		char t;
    		int x,y;
    		cin>>t>>x>>y;
    		if(t=='x')
    		{
    			change(x,y,1,n,1);
    		}
    		else if(t=='y')
    		{
    			printf("%d
    ",search(x,y,1,n,1));
    		}
    	}
    	return 0;
    }
    

    最后再给大家推荐几道线段树的养生的模板

    https://www.luogu.org/problem/U85066

    https://www.luogu.org/problem/P1637

  • 相关阅读:
    关于springMVC转换json出现的异常
    jQuery实现,动态自动定位弹窗。JS分页,Ajax请求
    servlet为什么要配置web.xml
    Jmeter系列(4)- Jmeter 脚本录制
    后缀数组模板
    NOIP2016 玩脱记
    TERSUS无代码开发(笔记21)-流程执行顺序思考(转载)
    ===>===>===>特色思TERSUS常用功能整理
    TERSUS无代码开发(笔记20)-本地开发测试mysql数据库连接
    TERSUS无代码开发(笔记19)-mysql-connector-java-5.-bin.jar下载方法
  • 原文地址:https://www.cnblogs.com/chen-1/p/11440708.html
Copyright © 2020-2023  润新知