zoukankan      html  css  js  c++  java
  • 【模板】线段树 1

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    1.将某区间每一个数加上x

    2.求出某区间每一个数的和

    输入输出格式

    输入格式:

    第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

    第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

    接下来M行每行包含3或4个整数,表示一个操作,具体如下:

    操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

    操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

    输出格式:

    输出包含若干行整数,即为所有操作2的结果。

    说明

    时空限制:1000ms,128M

    数据规模:

    对于30%的数据:N<=8,M<=10

    对于70%的数据:N<=1000,M<=10000

    对于100%的数据:N<=100000,M<=100000

    思路:

    明显,这道题是一道线段树的模板题(虽然可以拿分块什么的水过去),在题解之前,先普及一些知识

    什么是线段树?

    我们知道,一般数据的储存是线性的,但这样对于区间问题并不优,下图所示的数列

    我们如果想要

    查询,那么时间复杂度为o(区间长度)有些别跟我说用前缀和的可以优化到O(1)级别,没用的,前缀和的修改时间复杂度更高

    但修改呢??

    如果你用正常修改,时间复杂度为O(区间长度),如果你要用前缀和,那么复杂度会为O(数列长度-区间左端);

    显然,过于暴力,出题人想卡你的话你的时间复杂度会劣化到最大O(操作数*数列长度)

    TLE 的飞起

    那该怎么办呢?

    大家学过堆吧?

    一个O(nlogn^2)的暴力快排,上了二叉树就变成了O(nlogn)

    快了好多啊!

    那么我们这个操作能不能上树呢?

    当然可以

    线段树该出场了

    看下面这幅图

    我们成功通过二分区间的方式建立了一棵树

    此时查询,时间就省得多

    比如说查询1~12,暴力的话复杂度为O(12),现在只要查询【1~7】,【8~11】,【12】三个区间即可

    复杂度降到O(3log14);

    别看就降了这么一点,如果n很大呢?

    再说说插入

    你们可能会说,插入以前是O(n),你这一来成了O(nlogn),还费劲了

    谁说一定要插到底来着?我这么懒就不能插到中间吗?

    答案是肯定的,lazy标记该出场了

    比如说上图【4~11】每个加3

    结果如下

    很显然,如果一个区间被要添加的区间完全包含,那你就不用再去下放到他的儿子,在他的位置上打个lazy标记jike

    但同时你要注意细节,因为含有lazy标记的区间的子区间并没有被修改,所以在查询他的子区间时,lazy标记需要下放

    同时如果你修改的只是一个元素,那也不需要lazy标记,直接改值即可

    线段树的均摊复杂度为O(nlogn);

    线段树的实现有两种方式,数组法和存边法,前者较为简单,但后者在写一些高阶线段树(比如说主席树)和平衡树时更优

     我给出一组用数组解决的方法

    见代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    struct node{
    	long long lazy,sum;
    }tree[400010];
    int x[100010];
    int n,m,a,b,c;
    int as;
    int sx,st,sk;
    long long ans;
    char pd;
    void update(int l,int r,int i)//更新节点函数(就是下放函数) 
    {
    	if(!tree[i].lazy)
    	{
    		return;
    	}
    	int mid=(l+r)/2;
    	tree[i*2].sum+=tree[i].lazy*(mid-l+1);
    	tree[i*2+1].sum+=tree[i].lazy*(r-mid);
    	tree[i*2].lazy+=tree[i].lazy;
    	tree[i*2+1].lazy+=tree[i].lazy;
    	tree[i].lazy=0;
    }
    long long query(int tl,int tr,int l,int r,int i)//查询函数 
    {
    	if(tl>r||tr<l)
    	{
    		return 0;
    	}
    	if(tl<=l&&r<=tr)
    	{
    		return tree[i].sum;
    	}
    	update(l,r,i);
    	int mid=(l+r)/2;
    	return query(tl,tr,l,mid,i*2)+query(tl,tr,mid+1,r,i*2+1);
    }
    void add(int tl,int tr,int l,int r,int i,int val)//添加函数 
    {
    	if(tl>r||tr<l)
    	{
    		return;
    	}
    	if(tl<=l&&tr>=r)
    	{
    		tree[i].sum+=val*(r-l+1);
    		tree[i].lazy+=val;
    		return;
    	}
    	update(l,r,i);
    	int mid=(l+r)/2;
    	add(tl,tr,l,mid,i*2,val);
    	add(tl,tr,mid+1,r,i*2+1,val);
    	tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    }
    void build(int l,int r,int i)//运用初始数据建树 
    {
    	if(l==r)
    	{
    		tree[i].sum=x[l];
    		return;
    	}
    	int mid=(l+r)/2;
    	build(l,mid,i*2);
    	build(mid+1,r,i*2+1);
    	tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    }
    int main()
    {
    	cin>>n>>m;
    	for(a=1;a<=n;a++)
    	{
    		cin>>x[a];
    	}
    	build(1,n,1);
    	scanf("
    ");
    	for(int i=1;i<=m;i++)
    	{
    		char ch;
    		int l,r,v;
    		scanf("%c",&ch);
    		if(ch=='2')
    		{
    			scanf("%d%d
    ",&l,&r);
    			ans=query(l,r,1,n,1);
    			printf("%lld
    ",ans);
    		}
    		else
    		{
    			scanf("%d%d%d
    ",&l,&r,&v);
    			add(l,r,1,n,1,v);
    		}
    	}
    }
    
  • 相关阅读:
    Java StringBuilder、基本类型的包装类
    立个Flag不学好PHP誓不罢休
    LAMP搭建 转
    CentOS使用yum源中自带的rpm包安装LAMP环境
    CentOS RedHat YUM 源扩展补充(32位、64位均有)
    解决phpmyadmin上传文件大小限制的配置方法
    lanmp v2.5一键安装包发布(包括lamp,lnmp,lnamp安装)
    图像处理 jpg png gif svg
    NAT模式/路由模式/全路由模式 (转)
    网页制作中绝对路径和相对路径的区别
  • 原文地址:https://www.cnblogs.com/ztz11/p/8979210.html
Copyright © 2011-2022 走看看