线段树基础

官方介绍:

线段树是一种 二叉搜索树,与 区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点,对于线段树中的每一个非 叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是 平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
我对线段树的理解差不多就是有那么一个区间,一直二分到不能分,也就是下传到每一个元素本身,比如在上图的[1,10]区间我寻找3,这显然是二分搜索,如果我要给3加上5呢  那么显然不仅3本身要变,包含3的区间都需要加5。这种线段树的方法就大大降低了时间复杂度。 而线段树的的区间操作分为两种:单点更新和区间更新。

单点更新:

代码主要实现两个功能:1修改、2查询。

修改:

扫描二维码关注公众号,回复: 2184818 查看本文章

void modify(int p,int l,int r,int x,int v)
{
    a[p]+=v;
    if(l==r){
 return ;
 }
    int mid=(l+r)/2;
    if(mid>=x)modify(p*2,l,mid,x,v);
    else modify(p*2+1,mid+1,r,x,v);
}

这是一种写法,也可以用push_up将子结点的信息更新到父亲节点,代码如下 :

void up(int p)
{
 a[p]=a[p*2]+a[p*2+1];
}
void modify(int p,int l,int r,int x,int v)
{
    if(l==r){
     a[p]+=v;
 return ;
 }
    int mid=(l+r)/2;
    if(mid>=x)modify(p*2,l,mid,x,v);
    else modify(p*2+1,mid+1,r,x,v);
    up(p);
}

原先我更偏向第一种写法,但是后来我接触到后面的区间更新之后就改成第二种了,更形象,好理解些。

我也再次注释一下这些参数的意义,以备复习:p:当前的结点、l,r:所查询的区间的起点与终点,会更新、x,v:因为是修改自然少不了要修改的值和修改内容啦,x即为你要改的数,而v就是你要对x的操作内容,默认+。


查询:

int query(int p,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return a[p];//所查询区间为整个区间
    int mid=(l+r)/2,res=0;
    if(x<=mid) res+=query(p*2,l,mid,x,y);
    if(y>mid) res+=query(p*2+1,mid+1,r,x,y);
    return res;
}

没什么好说的,就是X,Y 为所查询区间,最后返回的res为所查询区间的内容——这个区间内所有元素之和。

基础知识介绍到这里  来一题模板题:



问题描述

有一种神奇斑点蛇,蛇如其名,全身都是斑点,斑点数量可以任意改变。
有一天,蒜头君十分的无聊,开始数蛇上的斑点。假设这条蛇的长度是N cm,蒜头君已经数完开始时蛇身的第i上有ai个斑点。
现在蒜头君想知道这条斑点蛇的任意区间的蛇身上一共有多少个斑点。这好像是一个很容易的
事情,但是这条蛇好像是和蒜头君过不去,总是刻意的改变蛇身上的斑点数量。
于是,蒜头君受不了了,加上蒜头君有密集型恐惧症。聪明又能干的你能帮帮他吗?
输入格式
第一行一个正整数 N(N≤50000)表示这条斑点蛇长度为 N 厘米,接下来有 N 个正整数,第 i 个正整数 ai​ 代表第 i 个斑点蛇第 i 厘米开始时有 ai​ 个斑点(1≤ai​≤50)。
接下来每行有一条命令,命令有 4 种形式:
(1) Add i j,i 和 j 为正整数,表示第 i 厘米增加 j 个斑点(j 不超过 30);
(2) Sub i j,i 和 j 为正整数,表示第 i 厘米减少 j 个斑点(j 不超过 30);
(3) Query i j,i 和 j 为正整数,i≤j,表示询问第 i 到第 j 厘米的斑点总数(包括第 i 厘米和第 j 厘米);
(4) End 表示结束,这条命令在每组数据最后出现;
最多有 40000 条命令。
输出格式
对于每个 Query 询问,输出一个整数并回车,表示询问的段中的总斑点数,这个数保证在int范围内。
样例输入
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End
样例输出
6
33
59


 
  


没什么好说的啊  模板题啊,直接把函数在上面一些就AC了呀

附上AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=50010;
int a[maxn*4+100];
int s[maxn*4+100];
void up(int p)
{
 a[p]=a[p*2]+a[p*2+1];
}
void modify(int p,int l,int r,int x,int v)
{
    if(l==r){
     a[p]+=v;
 return ;
 }
    int mid=(l+r)/2;
    if(mid>=x)modify(p*2,l,mid,x,v);
    else modify(p*2+1,mid+1,r,x,v);
    up(p);
}
int query(int p,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)return a[p];
    int mid=(l+r)/2,res=0;
    if(x<=mid) res+=query(p*2,l,mid,x,y);
    if(y>mid) res+=query(p*2+1,mid+1,r,x,y);
    return res;
}
int main()
{
    int n,d;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
       {
        scanf("%d",&d);
        modify(1,1,n,i,d);
    }
    string s;
   while(cin>>s){
    if(s=="End")break;
    int i,j;
    cin>>i>>j;
    if(s=="Query")    {
      int wyh=query(1,1,n,i,j);
      printf("%d\n",wyh);
    }
    if(s=="Add")    modify(1,1,n,i,j);
    if(s=="Sub")  modify(1,1,n,i,-j);
   }
  return 0;
}

值得一提的是:这线段树类的题目要把数组开到maxn的4倍以上。

猜你喜欢

转载自blog.csdn.net/consine926/article/details/81059728