zkw线段树学笔记

Step2(操作):

 线段树最经典(也不怕是我们为什么用这样……的线条树)的哪怕是查询修改操作了,时间复杂度比较平均,都为O(logn)。

2017年注定是一个不平常的小日子,即将迎接我们的凡全新的2018年,相信大部分口对2017年生许多之觉醒和感慨。在“时间之对象”跨年晚会上,逻辑思考讲到一个玩耍行业的故事,网易、小米、腾讯三贱还在紧张的也罢自己的“吃鸡”游戏上丝,目的是抱先发优势的红利。上半年最为火之一日游还是《王者荣耀》,下半年即使成为了吃鸡游戏。这些呢刚诠释了承诺书领所说的:“不一味是一个维度在快马加鞭,是浑世界还在抢起来。”这虽谈。未来势必是再次同次于的频率革命。前面说的豪门可能会发那个可怜,大及了全社会之大方向与一个行趋势。接下来我虽来言点多少之,2017年相信大家都曾感受及了内容付费这道热风,在我看来,内容付费不仅仅只是解决之凡有教无类的问题,而还多之是缓解了豪门获得知识的频率问题。获取知识以免不了要摆到阅读,想必在大家的眼中,看开就是星期泡一杯咖啡,躺在躺椅上沐浴冬日如愿以偿阳光之悠闲时分。但是就就是少数丁之好听,大部分总人口大都以的凡在挤公交车上、挤地铁之路上这些碎片化的日。在这些碎片化的日里而是怎看之吧?

Total:

zkw线段树

1.建树非常简单。

2.丝段树能干的,它还实施。(似乎是这般的)

3.再次多惊喜等你来打……

而是今底自每周可看2、3本书,并且能够了解及图书的精髓,形成协调之学识储备库。并且这些阅读基本上是以地铁及走的通勤时间内成功的。然而我是何许如何以这点碎片化的时去看的啊?

Last:

 以上就是zkw的极度基本内容,简单也非略,最后,来同样串zkw类

//powered by:spaceskynet 2017-03-06
const int maxn=1e5;
class zkw
{
public:
    zkw()
    {
        m=1;
    }
    void build(int n)//区间和
    {
        for(;m<n;m<<=1);
        //m<<=1;//避免查端点值出错
        for(int i=m+1;i<n+m+1;i++) scanf("%d",&a[i]);//从m+1叶子节点开始,避免查询[1,...]时出错。
        for(int i=m-1;i;--i) a[i]=a[i<<1]+a[i<<1|1];
        /**差分建树
        for(int i=m-1;i;--i)
        {
            a[i]=min(a[i<<1],a[i<<1|1]);
            a[i<<1]-=a[i],a[i<<1|1]-=a[i];
        }
        **/
    }
    int query(int l,int r)
    {
        int ans=0;
        for(l+=m-1,r+=m+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1) ans+=a[l^1];
            if(r&1) ans+=a[r^1];
        }
        /************
        int mid=max(l,r);
        while(mid)
        {
            ans+=a[mid>>=1];//差分时定要记住回归,不要只将差值max输出
        }
        *************/
        return ans;
    }
    void updata(int pos,int val)
    {
        a[pos+=m]+=val;
        while(pos)
        {
            a[pos>>=1]=a[pos<<1]+a[pos<<1|1];
        }
    }
    void intervalup(int l,int r,int val)
    {
        int tmp=0;
        for(l+=m-1,r+=m+1;l^r^1;l>>=1,r>>=1)
        {
            if(~l&1) a[l^1]+=val;
            if(r&1) a[r^1]+=val;
            tmp=min(a[l],a[l^1]),a[l]-=tmp,a[l^1]-=tmp,a[l>>1]+=tmp;
            tmp=min(a[r],a[r^1]),a[r]-=tmp,a[r^1]-=tmp,a[r>>1]+=tmp;
        }
        while(l) tmp=min(a[l],a[l^1]),a[l]-=tmp,a[l^1]-=tmp,a[l>>=1]+=tmp;
    }
    int point(int x)//差分单点查询
    {
        int ans=0;
        while(x) 
        {
            ans+=a[x],x>>=1;
        }
        return ans;
    }
private:
    int a[2*maxn+2],m;
};

 

老二、了解书本(作者简介、书本目录、书评)

缘何而失去打听一本书的也罢?想象你追一个向往之女孩,首先你势必会想尽办法了解其的主导信息以及喜好好,甚至将公“爱的恶势力”伸往了她身边的丁,从侧面了解其,然后才见面初步抗日持久的言情。了解书本的作用吗是为以后好层次阅读做铺垫。了解书本的自身为主的办法是:作者简介、书本目录、书评顿时三独面去开。首先了解作者的简介可以吃咱们了解及写书的背景,这样咱们于看之早晚就是会见由他们是时去思考问题,看问题啊会换得进一步深厚;然后看目录可以帮助我们蛮好之摸底及整治本书的架,在读书的下咱们的逻辑会更加的清;最后看书评,我大的法门是于简书上看读者们写的念后谢同大致的故事描述,或者以赢得APP找下发生没起板。这样经过就三只步骤,其实我们本着书籍是较的问询了,我们尽管可知看清这仍开该不该深读一下,还是只是做个了解。

区间查询 

查询区间和,暂且设间隔也[l,r]吧,zkw又同样特性,化为(l-1,r+1)开区里头计。

因此,这便是为什么输入时如果于m+1开始,避免查询[1,…]不时错,若使询问[0,……],下标需加上1。

int query(int l,int r)
{
    int ans=0;
    for(l+=m-1,r+=m+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) ans+=a[l^1];
        if(r&1) ans+=a[r^1];
    }
    return ans;
}

 ~l&1,意思是是否也子,对于兄弟节点的话,最低位为0或1,0为左儿子,1也右儿子,对于左端点
l来说,我们只有待往右侧合并更新ans(加上兄弟节点,也尽管是右手节点,l^1),而非随便那左手。

r&1,同理,意思是是否也儿子。

每次循环后转移向那父节点继续操作,出口也l^r^1,为什么?若lr匪也与一些或兄弟节点,l^r^1一定为true,否则在啊和一些还是兄弟节点时跳出循环。

求max

拿更新时+改为max就行了。

int query(int l,int r)
{
    int ans=0;
    for(l+=m-1,r+=m+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) ans=max(a[l^1],ans);
        if(r&1) ans=max(a[r^1],ans);
    }
    /************
    int mid=max(l,r);
    while(mid)
    {
        ans+=a[mid>>=1];//差分时定要记住回归,不要只将差值max输出
    }
    *************/
    return ans;
}

求min

将上述max改为min

其三、写下文章的大校(关于作者、关于本书、核心内容)

每当咱们读毕了相同本书只后,其实这尚不够,这样的看其实是十分爱忘的。所以我们得进行一个总结。我在每次看完成后会刻画下这本开的大意,大概在200许左右,然后存储到有道云笔记里。主要从三单方面来描写:关于作者、关于本书、核心内容。率先关于作者我们可形容下作者的简介、时代背景……;然后关于本书,可以描绘下书本的目的,比如所假设发表的眼光,能够帮助我们什么;最后核心内容,记录核心内容我便的做法是为此画在张上写下,或者用有些构思导图软件来记录,比如Xmind、Mindnode、幕布等软件。

间隔修改

 最简单易行的光距离加减(但自身为止说这),和距离查询同一,只要拿更新ans改也a[i]+=val,加上差分回归(需用差分)。

void intervalup(int l,int r,int val)
{
    int tmp=0;
    for(l+=m-1,r+=m+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) a[l^1]+=val;
        if(r&1) a[r^1]+=val;
        tmp=min(a[l],a[l^1]),a[l]-=tmp,a[l^1]-=tmp,a[l>>1]+=tmp;
        tmp=min(a[r],a[r^1]),a[r]-=tmp,a[r^1]-=tmp,a[r>>1]+=tmp;
    }
    while(l) tmp=min(a[l],a[l^1]),a[l]-=tmp,a[l^1]-=tmp,a[l>>=1]+=tmp;
}

 

诵读一本书要协调无得什么事物,那读一本书的是无另外的图,我们用发灵魂之翻阅有谈得来之判断力,书评和文章介绍只是别人的评说与感触。在念了每本书尽量写下自己的见地吧,相信来矣若的单意见的给,你念的开呢才会生灵魂。

说到底总结一下自家之季步读书方法:

1、列书单

2、了解书本

3、写下故事梗概

4、输出自己的理念

Step1(建树):

先是, style=”color: #ff0000;”>堆式储存是要。——《统计的能力》

或许不用几近说,用凡是zkw(重口味)的均等老特征,如下图:

 图片 1

同一、 style=”color: #ff0000;”>化为二进制后不难看出,叶子节点的父节点是它们的 style=”color: #ff0000;”>前缀。———>>也就是说,找父亲只有需要 style=”color: #ff0000;”>右变一各(>>)!

第二、相反,找大节点的叶子就不当移————>>左移一位也 style=”color: #ff0000;”>左儿子,再 style=”color: #ff0000;”>+1(或者 style=”color: #ff0000;”>|1)为 style=”color: #ff0000;”>右儿子

三、第n臃肿节点个数为 style=”color: #ff0000;”>2(n-1)

季、Last but not least,最底部节点个数为公 style=”color: #ff0000;”>实际最多足操作的累的个数(换个说法,应该好称为 style=”color: #ff0000;”>值域 style=”color: #000000;”>, style=”color: #ff0000;”>为2的次幂)。!!!( style=”color: #ff0000;”>最重要

 有了其,就足以起来踏上上理论化为具体的赫赫道路。

 实践开始!

骨子里,很多早晚数组中数都未是2的次幂,怎么惩罚?————>>直接开2的次幂就尽了,多之上空不要了。

style=”color: #000000;”>图片 2————《统计的能力》

一律、我们理解,最底部实际上存的凡原数组,同时鉴于堆式储存的特色,序号为是顺序排列的,也就是说————>>当我们需要查询或改动时,只待于原数组序号上助长一个屡,设为m吧。

如何求m

for(m=1;m<n;m<<=1);

 实际上,m否极其底部所有节点的父节点总数,所以就需要而m也1,不断左移,当m>=n时停止。

对此叶子直接输入,对于父节点从叶子(继承?区间和,最酷价值,最小值……)。

build函数轻松得出:

void build(int n)
{
    for(m=1;m<n;m<<=1);
    //m<<=1;//避免查端点值出错
    for(int i=m+1;i<n+m+1;i++) scanf("%d",&a[i]);//从m+1叶子节点开始,避免查询[1,...]时出错。
    for(int i=m-1;i;--i) a[i]=a[i<<1]+a[i<<1|1];//区间和
/*
for(int i=m-1;i;--i) a[i]=max(a[i<<1],a[i<<1|1]);//最大值
for(int i=m-1;i;--i) a[i]=min(a[i<<1],a[i<<1|1]);//最小值
*/
}

何以去用就有些碎片化的、嘈杂的辰展开快捷的看吧?这即是小凡今天所要讲的情。我当平年前受自己创制了一个计划,每周阅读一本书,然后带在这计划,我每天早起挤在8点钟早高峰的地铁1时到铺子,晚上7点挤在后高峰的地铁同钟头至下,两只小之流年里,我一个月每天还浸泡在学识之海洋里不能自拔,都不怎么被自己的毅力所伏了。但是到月末的上,我还尚无看本身了一本书,更吓人的是自我早已经忘记了我看罢之面前几章的情了,于是自己决然的舍了是计划。相信看就首文章的人数多数事实上与自家同都是动碎片化的时光来阅读,但是效率却不行低下,看了生一样段忘了齐一致段。

PS:

说从线段树,可谓是平拿辛酸泪(尽管自爱不释手打树状数组??)。代码较?(似乎是滴),TLE(屡见不鲜),递归建树(!!!),说实在,用(说到底lowbit差了些,你懂的)。

先期来平等犯线段树:

 

#include<iostream>
#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int maxn=2e4;
int segtree[maxn*4+10],array[maxn];
void build(int,int,int);
int query(int,int,int,int,int);
void updata(int,int,int,int,int);
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&array[i]);
    }
    build(1,1,n);
    ……………………
    return 0;
}
void build(int node,int begin,int end)//node结点,begin为array某子区间首标号,end为某子区间末标号。
{
    if(begin==end)
    {
        segtree[node]=array[begin];//首末相等,直接赋值。
    }
    else
    {
        build(2*node,begin,(begin+end)>>1);//区间长度不为一,左右递归。
        build(2*node,(begin+end)>>1+1,end);
        /*---------------区间min------------*/
        if(segtree[2*node]<=segtree[2*node+1])//max改为?>=?
        {
            segtree[node]=segtree[2*node];
        }
        else
        {
            segtree[node]=segtree[2*node+1];
        }
        /*-------------------------------*/

        /*---------------区间和------------*/
        segtree[node]=(segtree[2*node]+segtree[2*node+1]);
        /*---------------------------*/
    }
}
int query(int node,int begin,int end,int left,int right)
{
    int p1,p2;
    if(left>end||right<begin)
    {
        return -1;
    }
    if(left<=end&&right>=begin)
    {
        return segtree[node];
    }
    p1=query(2*node,begin,(begin+end)>>1,left,right);
    p2=query(2*node+1,(begin+end)>>1+1,end,left,right);
    if(p1==-1)
    {
        return p2;
    }
    if(p2==-1)
    {
        return p1;
    }
    if(p1<=p2)
    {
        return p1;
    }
    else
    {
        return p2;
    }
}
void updata(int node,int begin,int end,int pos,int val)
{
    if(begin==end)
    {
        segtree[node]+=val;
        return;
    }
    int m=(left+right)>>1+1;
    if(pos<=m)
        updata(2*node,begin,(begin+end)>>1,pos,val);
    else
        updata(2*node+1,(begin+end)>>1+1,end,pos,val);
    /*----MIN--*/
    segtree[node]=min(segtree[2*node],segtree[2*node+1]);
    /*---------*/
}

 真长(还尚未写了)!!!

当自家于不久前,看到了zkw(不要误会),自之社会风气豁然开朗,代码王者荣耀归来,多年守望简洁先锋,终于get到zkw神器。(实际上我只玩MC)

 zkw(张昆玮大神):男,身处清华大学。zkw线段树出自《统计的力》。

一、列书单

排书单,这个点便像妈妈时常教育我们好好学习一样,基本上有的阅读方法都见面波及这一个读之前提。在当下之前,我像一个懵懂的女孩儿一样,天真无知,总想方尝去打破这些阅读之克,最后之结果是本人看的书写为无计划,看罢之写基本上就是忘记一大半了。之后我开尝试着列书单,制定每个月份的计划,有了书单之后,我之翻阅计划呢易得更其切实和清了。

单点查询

单点a[q]?直接查[q,q]免纵尽了呗。对,这是尚未错。但咱只要以它换得复杂一些(并无是干笑,说实在的,谁愿意写长篇大论的code),这是以RMQ做准备,这是咱需要一个神奇之东西——差分,把子节点所抱的价值维护为与那个父节点的差值。

 所以,build函数需要改:

void build(int n)//区间和
{
    for(;m<n;m<<=1);
    //m<<=1;//避免查端点值出错
    for(int i=m+1;i<n+m+1;i++) scanf("%d",&a[i]);//从m+1叶子节点开始,避免查询[1,...]时出错。
    for(int i=m-1;i;--i) 
    {
        a[i]=min(a[i<<1],a[i<<1|1]);
        a[i<<1]-=a[i],a[i<<1|1]-=a[i];
    }
}

 

这儿底询问,只待由叶子节点不断循环加上父节点直到根节点。

 

int point(int x)
{
    int ans=0;
    while(x) 
    {
        ans+=a[x],x>>=1;
    }
    return ans;
}

单点修改

先是说说单点修改。

我们还理解,线段树是由臻至下遍历的,而zkw是因为可以直接找到叶子,是出于下及直达遍历的,因此避免了不少剩余的看,对于只有点修改,我们只是需要将用修改的点找到(加上m),并循环使用至温馨、父节点直到根节点,方便了广大。

void updata(int pos,int val)
{
    a[pos+=m]+=val;
    while(pos)
    {
        a[pos>>=1]=a[pos<<1]+a[pos<<1|1];
    }
}

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website