【原创】 线段树学习总结

    经过了这几天的学习---线段树---收获着实不小,不过解稍微难一点的题还是会有超时的可能,但也算是入门了。我记得在这段时间的学习中,我保持了一个良好的习惯---刚开始的时候不要在网上搜解题报告。这样可以训练你的思考能力,有时候肯能还会和别人的不一样,学习起来就不是那么死板了。这几天的学习大致可以分为4个阶段(区间统计,单点更新,区间更新,区间合并,扫描线(这个只了解到了初级的))。

区间统计:
    这类型的题目不对节点更新,只是实现的统计的功能(有时候也会伴随着一些更新)。比如统计一个区间的最大值(最小值),统计逆序数(当然树状数组更好一些)等这个题一般的做法是在建树的同时,利用回溯的过程就把区间的信息统计出来。这类题适合于初学者。
    相应的练习题:
    hdu 1394  Minimum Inversion Number
    hdu 1698 Just a Hook
    hdu 2492 Ping pong
单点更新:
    其实单点更新顾名思义就是每次操作的时候对叶子节点操作,但是常规的访问都是以区间的形式。做这类题的时候只需要在更新的时候用update(index)实现更新就可以了,沿着叶子节点向上更新,用递归实现比较简单。这类题适合于初学者。
hdu 1754 I hate it
代码:
#include<stdio.h>
#define MAXN 200005

struct Tree
{
        int Max,l,r,m;
}ar[4*MAXN];

int n,m,h[MAXN];

int BuildTree(int index,int begin,int end)
{
        int x,y;
        ar[index].l=begin;ar[index].r=end;
        ar[index].m=(begin+end)>>1;
        if(begin<end)
        {
                x=BuildTree(2*index,begin,ar[index].m);
                y=BuildTree(2*index+1,ar[index].m+1,end);
                ar[index].Max=x>y?x:y;
        }
        else
                ar[index].Max=h[begin];
        return ar[index].Max;
}

int GetMax(int index,int begin,int end)
{
        if(ar[index].l==begin && ar[index].r==end)
        {
                return ar[index].Max;
        }
        else if(begin>ar[index].m)
        {
                return GetMax(index*2+1,begin,end);
        }
        else if(end<=ar[index].m)
        {
                return GetMax(index*2,begin,end);
        }
        int x,y;
        x=GetMax(index*2,begin,ar[index].m);
        y=GetMax(index*2+1,ar[index].m+1,end);
        return x>y?x:y;
}

void ChangeTree(int index,int x,int y)
{
        if(ar[index].Max<y)
        {
                ar[index].Max=y;
        }
        if(ar[index].l<ar[index].r)
        {
                if(x<=ar[index].m)
                {
                        ChangeTree(index*2,x,y);
                }
                else
                {
                        ChangeTree(index*2+1,x,y);
                }
        }
}

void init()
{
        int i;
        for(i=1;i<=n;i++)
        {
                scanf("%d",&h[i]);
        }
        BuildTree(1,1,n);
}

void make()
{
        char op[10];
        int x,y;
        while(m--)
        {
                scanf("%s%d%d",op,&x,&y);
                if(op[0]=='Q')
                {
                        printf("%d\n",GetMax(1,x,y));
                }
                else
                {
                        ChangeTree(1,x,y);
                }
        }
}

int main()
{
        while(scanf("%d%d",&n,&m)!=EOF)
        {
                init();
                make();
        }
        return 0;
}
相应的练习题:
hdu 1166 敌兵布阵
区间更新:

    刚开始的时候,可能有人会想到是否可以用单点更新的思想来做呢?按理来说是可以的,但是有时候往往我们代码的效率就不够好了。这样想,我们可以之间更新区间,但是当我们访问区间下面的节点的时候,不是已经改变了吗?所以我们还得在查询的时候,相应的更新。也叫做延迟更新。这类题适合于中等偏下的题,花不了多少时间。
相应的练习题:
hdu 4107 Gangster
hdu 3397 Sequence operation
区间合并:

    这类题最常见的题是:求一个区间连续的一些性质,比如1-n数组里,要么放黑棋,要么放白棋,然后给你一个询问区间[i,j],问你这个区间里黑棋连续最多的为多少。比如最大连续上升(很多时候有伴随着一些改变)等。常见的解法是:在节点里添加连个变量,l,r分别记录,左右连续的个数,然后在依次不断的向上,不断的更新。
这类题比较多,给一个题的代码:
hdu 3308 LCIS
 
  
#include<stdio.h>
#define MAXN 100005

struct data
{
        int l1,l2,r2,r1;
};

struct node
{
        int begin,end,mid;
        int num;
        data p;
}tree[MAXN<<2];

int n,m,st[MAXN],ans;

void SetValue(data &p,int l1,int l2,int r2,int r1)
{
        p.l1=l1;
        p.l2=l2;
        p.r2=r2;
        p.r1=r1;
}

int Max(int x,int y)
{
        return x>y?x:y;
}

void update(int index)
{
        int x=0;
        data p=tree[index<<1].p,q=tree[index<<1 | 1].p;
        if(st[p.r1]<st[q.l1])
        {
                x=q.l2-p.r2+1;
        }
        tree[index].num=Max(x,Max(tree[index<<1].num,tree[index<<1 | 1].num));
        if(p.l2+1==q.l1 && st[p.l2]<st[q.l1])
        {
                p.l2=q.l2;
        }
        if(p.r1+1==q.r2 && st[p.r1]<st[q.r2])
        {
                q.r2=p.r2;
        }
        p.r2=q.r2;
        p.r1=q.r1;
        tree[index].p=p;
}

void BuildTree(int index,int begin,int end)
{
        tree[index].begin=begin;tree[index].end=end;
        tree[index].mid=(begin+end)>>1;
        if(begin<end)
        {
                int x=0;
                BuildTree(index<<1,begin,tree[index].mid);
                BuildTree(index<<1 | 1,tree[index].mid+1,end);
                update(index);
        }
        else
        {
                tree[index].num=1;
                scanf("%d",&st[end]);
                SetValue(tree[index].p,end,end,end,end);
        }
}

void Insert(int index,int x)
{
        if(tree[index].begin==tree[index].end)
        {
                return;
        }
        if(x<=tree[index].mid)
        {
                Insert(index<<1,x);
        }
        else
        {
                Insert(index<<1 | 1,x);
        }
        update(index);
}

data get(int index,int begin,int end)
{
        data p,q;
        if(tree[index].begin==begin && tree[index].end==end)
        {
                if(ans<tree[index].num)
                {
                        ans=tree[index].num;
                }
                return tree[index].p;
        }
        if(end<=tree[index].mid)
        {
                return get(index<<1,begin,end);
        }
        else if(begin>tree[index].mid)
        {
                return get(index<<1 | 1,begin,end);
        }
        p=get(index<<1,begin,tree[index].mid);
        q=get(index<<1 | 1,tree[index].mid+1,end);
        int x=0;
        if(st[p.r1]<st[q.l1])
        {
                x=q.l2-p.r2+1;
        }
        ans=Max(x,ans);
        if(p.l2+1==q.l1 && st[p.l2]<st[q.l1])
        {
                p.l2=q.l2;
        }
        if(p.r1+1==q.r2 && st[p.r1]<st[q.r2])
        {
                q.r2=p.r2;
        }
        p.r2=q.r2;
        p.r1=q.r1;
        return p;
}

void make()
{
        BuildTree(1,0,n-1);
        int x,y;
        char op[5];
        while(m--)
        {
                scanf("%s%d%d",op,&x,&y);
                if(op[0]=='Q')
                {
                        ans=0;
                        get(1,x,y);
                        printf("%d\n",ans);
                }
                else
                {
                        st[x]=y;
                        Insert(1,x);
                }
        }
}

int main()
{
        int t;
        scanf("%d",&t);
        while(t--)
        {
                scanf("%d%d",&n,&m);
                make();
        }
        return 0;
}
相应的练习题:
hdu 3911 Back And White
hdu 4046 Panda
扫描线:
    这一阶段主要求的是一些矩形的面积并,周长并,有时候也会有体积并。做这类的题,一般会用到离散化的思想。比如你要插入[4999,6000]这个区间到线段树中,那么你只需要把4999和6000分别映射到两个更小的数x,y然后插入。然后一次扫描线段就可以了。
今天上午写了一个代码:
hdu 1255 覆盖的面积
 
  
#include<stdio.h>
#include<cmath>
#include<algorithm>
#define MAXN 2005

using namespace std;

double flag[MAXN];

struct data
{
        double y1,y2,x,f;
}line[MAXN];

struct node
{
        int begin,end,mid,cover;
        double x;
}st[MAXN<<2];

int n;

int cmpdouble(double x,double y)
{
        return x<y;
}

int cmpd(data p,data q)
{
        return p.x<q.x;
}

void BuildTree(int t,int begin,int end)
{
        st[t].begin=begin;st[t].end=end;
        st[t].mid=(begin+end)>>1;
        st[t].cover=0;
        st[t].x=0;
        if(begin>=end-1)
                return;
        BuildTree(t<<1,begin,st[t].mid);
        BuildTree(t<<1 | 1,st[t].mid,end);
}

void init()
{
        int i,l,r;
        double x1,x2,y1,y2;
        for(i=0;i<n;i++)
        {
                scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
                l=i<<1;r=l+1;
                flag[l]=y1;
                line[l].x=x1;
                line[l].y1=y1;
                line[l].y2=y2;
                line[l].f=1;
                flag[r]=y2;
                line[r].x=x2;
                line[r].y1=y1;
                line[r].y2=y2;
                line[r].f=-1;
        }
        n=r;
        sort(flag,flag+n+1,cmpdouble);
        sort(line,line+n+1,cmpd);
        BuildTree(1,0,n);
}

int search(double x)
{
        int l=0,r=n,mid;
        double y;
        while(l<=r)
        {
                mid=(l+r)>>1;
                if(fabs(flag[mid]-x)<1e-8)
                        return mid;
                else if(flag[mid]<x)
                {
                        l=mid+1;
                }
                else
                {
                        r=mid-1;
                }
        }
        return -1;
}

double update(int t,int begin,int end,double x,int f)
{

        if(begin>=st[t].end || end<=st[t].begin)
                return 0;
        if(st[t].begin+1==st[t].end)
        {
                double ans;
                if(st[t].cover>1)
                {
                        ans=(x-st[t].x)*(flag[st[t].end]-flag[st[t].begin]);
                        st[t].x=x;
                        st[t].cover+=f;
                        return ans;
                }
                else
                {
                        st[t].cover+=f;
                        st[t].x=x;
                        return 0;
                }
        }
        return update(t<<1,begin,end,x,f)+
                update(t<<1 | 1,begin,end,x,f);
}
void make()
{
        double sum=0;
        int i,l,r;
        for(i=0;i<n;i++)
        {

                l=search(line[i].y1);
                r=search(line[i].y2);
                sum+=update(1,l,r,line[i].x,line[i].f);
        }
        printf("%.2lf\n",sum);
}

int main()
{
        int t;
        scanf("%d",&t);
        while(t--)
        {
                scanf("%d",&n);
                init();
                make();
        }
        return 0;
}
其实还可以优化,留给自己慢慢完成!!!!
相应的练习:
hdu 3642 Get The Treasury(体积合并)
    这几天的练习总体说来效果还是可以,至少解决了很多自己不能完成的东西。其实还有其他的线段树解法,它不是常规的。比如:hdu 4007 Dave。
    第一次近距离接触线段树,希望自己后面能更好的理解线段树。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用的内容,这段引用中包含了一些Vue.js代码和HTML代码,并讨论了换行符和空格在网页中的使用。其中,引用是关于添加和还原换行符的方法,引用是关于在编辑数据时获取详情和使用textarea的示例代码,引用是关于HTML中空格的处理和字符实体的使用。 问题中提到的&nbsp;是HTML中表示空格的字符实体。HTML通常会自动截取多余的空格,不管你加多少空格,都被看做一个空格。为了在网页中增加空格,可以使用&nbsp;表示空格。此外,还有其他常用的字符实体,如<表示小于号,>表示大于号,&表示&符号,"表示双引号,等等。 需要注意的是,引用的内容中有一些HTML实体被转义了,所以在描述字符实体时需要参考编码转义后的字符。 根据给定的引用内容,我无法直接回答问题,因为问题中的信息不完整。请提供更具体的问题或补充说明,我将尽力为您提供帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [js+v-html+正则表达式去除所有& nbsp和html标签(& nbsp 换行 )](https://blog.csdn.net/xm_w_xm/article/details/91992754)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [html&nbsp;&nbsp;特殊字符(转)](https://blog.csdn.net/feiniu5566/article/details/13624619)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值