题目链接:点击查看

题目大意:给出n个连续的空房间,依次进行m个操作,操作一是查询操作,查询在总区间内的一段连续的长度为x的空房间,并

且该位置要靠左,如果查询到返回最左边的端点,并将其占用,找不到返回0。操作二是更新操作,更新以x为起点,长度为len的

房间为空房间

题目分析:这个题目虽然明白是要使用线段树,但是却无从下手,还是没办法将题目抽象为模型来想,还需要多加训练才行啊!

在找题解的时候看到一个大佬的感悟我觉得很赞同:做线段树的题目不能为了做题而做题,线段树是一种数据结构,是为了解决

问题而产生的,所以具体问题需要具体分析,因此在做题的时候,需要分析题目的本质,然后在思考该怎么使用线段树去解决问

题,而不是单纯的就这题解出发,如果时间长了,会陷入思维定势,遇到变形的题目反而一点头绪都没有。

回到这个题目,这个题目的查询操作和更新操作都可以对应着线段树的区间查询和区间更新来完成,但这个题的一个难点是如查

找最左端的符合条件的区间。

那么这里就针对于查找的函数来详细解释一下吧,目前做线段树和区间合并的问题的时候,感觉难点并不是pushup函数,反而是

查找函数,一般的变化都出在这上面。


对于本题的query函数,我们因为需要查找最左端满足条件的区间,所以我们现在先枚举出三个满足条件的区间:

设k为当前节点,tree为线段树的数组,all为区间中最长的连续空房间数量,rr为区间中从右端点开始的最长连续空房间数量,

ll为区间中从左端点开始的最长连续空房间数量

那么假设我们将每个节点都分为左右两个节点:

  1. tree[k<<1].all(左节点中最长的连续空房间数量)
  2. tree[k<<1].rr+tree[k<<1|1].ll(跨越中点时最长的连续房间的数量)
  3. tree[k<<1|1].all(右节点中最长的连续空房间数量)

按照从一到二到三枚举条件,若满足向下递归,若不满足返回该返回的值,若三个条件都不满足则返回0,可以设置剪枝,即当

这个区间的最大长度为0的时候可以直接返回,因为接下来的这三个判断都毫无意义了。

上代码把,我自己写的代码很乱,然后看到网上大佬的博客后适当封装了一下,稍微能好看一点,都挂上吧:

蒟蒻的代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<sstream>
#include<cmath>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;struct Node
{int l,r;int ll,rr,all;int lazy;
}tree[N<<2];void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].all=tree[k].ll=tree[k].rr=r-l+1;tree[k].lazy=-1;if(l==r)return;int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}void pushup(int k)
{tree[k].ll=tree[k<<1].ll;if(tree[k].ll==tree[k<<1].r-tree[k<<1].l+1)tree[k].ll+=tree[k<<1|1].ll;tree[k].rr=tree[k<<1|1].rr;if(tree[k].rr==tree[k<<1|1].r-tree[k<<1|1].l+1)tree[k].rr+=tree[k<<1].rr;tree[k].all=max(tree[k<<1].rr+tree[k<<1|1].ll,max(tree[k<<1].all,tree[k<<1|1].all));
}void pushdown(int k)
{tree[k<<1].lazy=tree[k<<1|1].lazy=tree[k].lazy;tree[k<<1].all=tree[k<<1].ll=tree[k<<1].rr=tree[k<<1].lazy*(tree[k<<1].r-tree[k<<1].l+1);tree[k<<1|1].all=tree[k<<1|1].ll=tree[k<<1|1].rr=tree[k<<1|1].lazy*(tree[k<<1|1].r-tree[k<<1|1].l+1);tree[k].lazy=-1;
}void update(int k,int l,int r,int val)
{if(tree[k].r<l||tree[k].l>r)return;if(tree[k].r<=r&&tree[k].l>=l){tree[k].all=tree[k].ll=tree[k].rr=val*(tree[k].r-tree[k].l+1);tree[k].lazy=val;return;}if(tree[k].lazy!=-1)pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);
}int query(int k,int len)
{if(tree[k].l==tree[k].r)//对叶子节点特判,不然过不了len为1的区间,不过这个题可能是数据水,不特判也能过。。if(len==1)return tree[k].ll;if(tree[k].all==0)//剪枝return 0;if(tree[k].lazy!=-1)pushdown(k);if(tree[k<<1].all>=len)//上述左区间return query(k<<1,len);else if(tree[k<<1].rr+tree[k<<1|1].ll>=len)//上述跨越中间点的区间return tree[k<<1].r-tree[k<<1].rr+1;else if(tree[k<<1|1].all>=len)//上述右区间return query(k<<1|1,len);else//没有满足的区间return 0;
}int main()
{
//  freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){build(1,1,n);while(m--){int a;scanf("%d",&a);if(a==1){int x;scanf("%d",&x);int temp=query(1,x);cout<<temp<<endl;if(temp)update(1,temp,temp+x-1,0);}else{int x,len;scanf("%d%d",&x,&len);update(1,x,x+len-1,1);}}}return 0;
}

照着大佬的代码封装了一下:(主要是pushup函数和pushdown函数看起来清爽了一点)

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<sstream>
#include<cmath>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;struct Node
{int l,r;int ll,rr,all;int lazy;int mid(){return (l+r)>>1;}int cal_len(){return r-l+1;}void update_len(){all=rr=ll=lazy?cal_len():0;}
}tree[N<<2];void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].all=tree[k].ll=tree[k].rr=tree[k].cal_len();tree[k].lazy=-1;if(l==r)return;int mid=tree[k].mid();build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}void pushup(int k)
{tree[k].ll=tree[k<<1].ll;if(tree[k].ll==tree[k<<1].cal_len())tree[k].ll+=tree[k<<1|1].ll;tree[k].rr=tree[k<<1|1].rr;if(tree[k].rr==tree[k<<1|1].cal_len())tree[k].rr+=tree[k<<1].rr;tree[k].all=max(tree[k<<1].rr+tree[k<<1|1].ll,max(tree[k<<1].all,tree[k<<1|1].all));
}void pushdown(int k)
{tree[k<<1].lazy=tree[k<<1|1].lazy=tree[k].lazy;tree[k<<1].update_len();tree[k<<1|1].update_len();tree[k].lazy=-1;
}void update(int k,int l,int r,int val)
{if(tree[k].r<l||tree[k].l>r)return;if(tree[k].r<=r&&tree[k].l>=l){tree[k].lazy=val;tree[k].update_len();return;}if(tree[k].lazy!=-1)pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);
}int query(int k,int len)
{if(tree[k].l==tree[k].r)//对叶子节点特判,不然过不了len为1的区间,不过这个题可能是数据水,不特判也能过。。if(len==1)return tree[k].ll;if(tree[k].all==0)//剪枝return 0;if(tree[k].lazy!=-1)pushdown(k);if(tree[k<<1].all>=len)//上述左区间return query(k<<1,len);else if(tree[k<<1].rr+tree[k<<1|1].ll>=len)//上述跨越中间点的区间return tree[k<<1].r-tree[k<<1].rr+1;else if(tree[k<<1|1].all>=len)//上述右区间return query(k<<1|1,len);else//没有满足的区间return 0;
}int main()
{
//  freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){build(1,1,n);while(m--){int a;scanf("%d",&a);if(a==1){int x;scanf("%d",&x);int temp=query(1,x);cout<<temp<<endl;if(temp)update(1,temp,temp+x-1,0);}else{int x,len;scanf("%d%d",&x,&len);update(1,x,x+len-1,1);}}}return 0;
}

HDU - 3667 Hotel(线段树+区间合并)相关推荐

  1. POJ 3667 Hotel 线段树区间合并

    线段树的区间合并,其中lsum代表区间左边最大连续长度,rsum代表区间最大连续长度,msum代表区间 最大连续长度.因为本题是查询连续区间的左端点,那么如果左儿子满足条件,就继续查询左儿子,左儿 子 ...

  2. hdu 3308 LCIS 线段树 + 区间合并

    传送门 文章目录 题意: 思路: 题意: 思路: 日常水一篇题解. 带修改的求区间连续的递增序列,我们考虑用线段树维护. 直接维护mlenmlenmlen是区间最长的递增序列,lslsls是从左端点开 ...

  3. hdu.3308 LCIS(线段树,区间合并+单点更新)

    按照傻崽大神的线段树修炼路线,自己做的第二道区间合并的题. 问题比较简单明了,区间求最长连续上升子序列,但是是需要单点更新的 n个数, m组操作 Q A B 询问[A,B]区间的最长连续上升子序列: ...

  4. HDU - 5316 Magician(线段树区间合并)

    题目链接:点击查看 题目大意:给出长度为 n 的数列 a ,接下来进行 m 次操作,每次操作分为两种类型: 0 l r:询问区间 [ l , r ] 内的最长子序列之和,要求相邻两个位置的下标奇偶性不 ...

  5. Tunnel Warfare(HDU1540+线段树+区间合并)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1540 题目: 题意:总共有n个村庄,有q次操作,每次操作分为摧毁一座村庄,修复一座村庄,和查询与询问的 ...

  6. poj-3667(线段树区间合并)

    题目链接:传送门 参考文章:传送门 思路:线段树区间合并问题,每次查询到满足线段树的区间最左值,然后更新线段树. #include<iostream> #include<cstdio ...

  7. HDU3308 线段树区间合并

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 ,简单的线段树区间合并. 线段树的区间合并:一般是要求求最长连续区间,在PushUp()函数中实 ...

  8. 树链剖分——线段树区间合并bzoj染色

    线段树区间合并就挺麻烦了,再套个树链就更加鬼畜,不过除了代码量大就没什么其他的了.. 一些细节:线段树每个结点用结构体保存,pushup等合并函数改成返回一个结构体,这样好写一些 struct Seg ...

  9. SPOJ GSS3-Can you answer these queries III-分治+线段树区间合并

    Can you answer these queries III SPOJ - GSS3 这道题和洛谷的小白逛公园一样的题目. 传送门: 洛谷 P4513 小白逛公园-区间最大子段和-分治+线段树区间 ...

最新文章

  1. http 里面的post和get区别
  2. Keepalived的LVS配置
  3. 编译时和运行时、OC中对象的动态编译机制
  4. 上海教师中级职称英语计算机考试,计算机教师如果考过了软考中级对职称评定有用吗,学校会承认嘛,有人懂吗,求助...
  5. 非cpu0启动linux,SD卡无法启动Linux的问题及解决
  6. 平流式隔油池计算_海淀区平流式隔油池厂家供货
  7. 合理使用EntityFramework数据验证的异常错误提示信息
  8. SharePoint 2013 文档库中PPT转换PDF
  9. XproerIM-V1,2,12,65475发布。
  10. Spark-on-YARN
  11. vs2005 虚拟调试配置(转)
  12. Expression Blend实例中文教程(5) - 布局控件快速入门StackPanel,ScrollViewer和Border
  13. 神策分析 2.1 重磅上线!报警诊断、多主体分析、渠道追踪等赋能企业数字化经营...
  14. 平面设计专业学什么?大学平面设计主修课程有哪些?
  15. ios开发常用RGB色值
  16. 使用扩展卡尔曼滤波(EKF)进行AHRS九轴姿态融合
  17. 用于数据科学和机器学习的GitHub存储库和Reddit主题
  18. QString汉字个数检测
  19. 仿泡团影视网源码 苹果cmsV8版本 电脑端+影视模块
  20. tf data 切换数据集 使用并行提高效率

热门文章

  1. 延迟队列Delay Queue
  2. 分布式ID-数据库多主模式
  3. rabbitmq常用配置
  4. 幻读(phantom read)
  5. RPC创建API 模块
  6. 锁的释放流程-unparkSuccessor
  7. ReentrantLock.nofairTryAcquire
  8. SpringMVC的数据响应-页面跳转-返回ModelAndView形式1(应用)
  9. Response_案例4_验证码_点击切换
  10. 代理模式coding-动态代理