这个题题意我大概解释一下,就是一开始一条直线,上面的点全是联通的,有三种操作

1.操作D把从左往右第x个村庄摧毁,然后断开两边的联通。

2.询问Q节点相联通的最长长度

3.把最后破坏的村庄重建。

这个其实也是非常典型的线段树区间合并,正好可以学一下。

我们给线段树的结点赋予5个值,l 区间左端点, r 区间右端点, ls从左端点开始的最大连续个数(左连续),rs从右端点开始最大的连续个数,ms这个节点所表示的区间内部,最大的连续个数。

然后我们考虑建树,由于最开始是相通的,因此这些值初始为r-l+1

然后常规操作单点修改,现在考试如何维护的:

首先是左儿子节点的左连续传给父节点的左连续,右儿子的右连续传给父节点的右连续

然后是维护父节点的最大连续,父节点的最大连续有可能由三个部分得到:两个儿子节点的分离的最大连续,以及合并左儿子的右连续和右儿子的左连续。因此把这三个值的最大值拿出来即可。

然后考虑询问:

如果询问到叶节点,或者区间内部最大的连续区间为0(代表区间内全是0)或者区间值等于区间长度(区间满了)这些可以直接退出循环。

让后我们考虑询问分裂的时候的问题,如果我们询问的点在树的左边,我们会发现有两种情况:

如果这个点是大于在树的左儿子的右连续的左边界,意味着这个点联通着树的右节点,我们需要在询问左节点的同时,加上右节点的右连续,即t>=tree[L(root)].r-tree[L(root)].rs+1

否则我们只需要访问左节点,继续询问即可。

反之也是如此,从而实现了线段树的分裂和合并

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int maxx = 1e5+;
int s[maxx];
inline int L(int r){return r<<;};
inline int R(int r){return r<<|;};
inline int MID(int l,int r){return (l+r)>>;};
struct node{
int l,r;
int ls,rs,ms;//左段开始最大连续区间,右端开始最大连续区间,节点区间内最大连续区间
}tree[maxx<<];
void buildtree(int root,int l,int r){
tree[root].l=l;
tree[root].r=r;
tree[root].ls=(r-l+);
tree[root].rs=(r-l+);
tree[root].ms=(r-l+);
if(l==r){
return;
}
int mid=MID(l,r);
buildtree(L(root),l,mid);
buildtree(R(root),mid+,r);
}
void update(int root,int t,int op){
int l=tree[root].l;
int r=tree[root].r;
if(l==r)//到叶节点
{
if (op==){//如果是销毁操作
tree[root].ls=tree[root].rs=tree[root].ms=;
}else{
tree[root].ls=tree[root].rs=tree[root].ms=;//恢复
}
return;
}
int mid=MID(l,r);
if (t<=mid){
update(L(root),t,op);
}else{
update(R(root),t,op);
}
tree[root].ls=tree[L(root)].ls;//把左儿子节点的左连续传给父亲节点
tree[root].rs=tree[R(root)].rs;//把右儿子节点的右连续传给父亲节点
tree[root].ms=max(max(tree[L(root)].ms,tree[R(root)].ms),tree[L(root)].rs+tree[R(root)].ls);
//父亲节点区间内的最大连续是由三部分构成,左右儿子的最大连续,以及左儿子的最大右连续加上右儿子的最大左连续
if (tree[L(root)].ls == tree[L(root)].r-tree[L(root)].l+)//如果左儿子区间满了,
tree[root].ls+=tree[R(root)].ls;//节点的左连续应该加上右儿子的左连续
if (tree[R(root)].rs == tree[R(root)].r-tree[R(root)].l+)//同理右儿子节点满了
tree[root].rs+=tree[L(root)].rs;//节点的右连续应该加上左儿子的右连续
}
int query(int root,int t)
{
int l=tree[root].l;
int r=tree[root].r;
if(l==r || tree[root].ms == || tree[root].ms==r-l+){
//到达一个叶子节点//或者里面全是呗摧毁的点//或者是区间已满的点
return tree[root].ms;
}
int mid=MID(l,r);
if (t<=mid)
{
if(t>=tree[L(root)].r-tree[L(root)].rs+)//t节点在看左子树,tree[2*i].r-tree[2*i].rs+1代表左子树右边连续区间的左边界值,如果t在左子树的右区间内,则要看右子树的左区间有多长并返回
return query(L(root),t)+query(R(root),mid+);
else
query(L(root),t);//如果不在左子树的右边界内,则只需要看左子树
}else
{
if (t<=tree[R(root)].l+tree[R(root)].ls-)//看右子树的左连续的右边界,如果t在在这个范围内,需要再次访问其从左儿子右边界开始的右连续从mid开始
return query(R(root),t)+query(L(root),mid);
else
return query(R(root),t);
}
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
buildtree(,,n);
char op;
int tmp;
int top;
while(m--){
scanf(" %c",&op);
if (op=='D'){
scanf("%d",&tmp);
s[top++]=tmp;
update(,tmp,);
}else if(op=='Q'){
scanf("%d",&tmp);
printf("%d\n",query(,tmp));
}else
{
if(tmp>)
{
tmp=s[--top];
update(,tmp,);
}
}
}
}
return ;
}

HDU - 1540 线段树的合并的更多相关文章

  1. HDU 3911 线段树区间合并、异或取反操作

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=3911 线段树区间合并的题目,解释一下代码中声明数组的作用: m1是区间内连续1的最长长度,m0是区间内连续 ...

  2. hdu 3308(线段树区间合并)

    LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  3. HDU 3911 线段树区间合并

    北京赛区快了,准备袭击数据结构和图论.倒计时 18天,线段树区间合并.维护一个最长连续.. 题意:给一个01串,以下有一些操作,问区间最长的连续的1的个数 思路:非常裸的线段树区间合并 #includ ...

  4. hdu 1540 线段树

    这题的意思是现在有一些村庄成一条直线排列,现在有三个操作,D:摧毁一个指定的村庄,Q:询问与指定村庄相连的村庄个数, 就是这个村庄向左和向右数村庄数量,遇到尽头或损坏的村庄为止,这个就是与这个村庄相连 ...

  5. HDU 1540<线段树,区间并>

    题目连接 参考 题意: 维护各个点的连续的最大连续长度. 思路: 主要是维护一个区间的三个变量ll,f[i].l为起点向右的最大连续 长度,rl:f[i].r为起点向左的最大连续长度,ml:[l,r] ...

  6. I - Tunnel Warfare HDU - 1540 线段树最大连续区间

    题意  :一段区间  操作1 切断点 操作2 恢复最近切断的一个点 操作3 单点查询该点所在最大连续区间 思路:  主要是push_up :  设区间x 为母区间  x<<1 ,x< ...

  7. hdu 1806(线段树区间合并)

    Frequent values Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  8. hdu 3308 线段树 区间合并+单点更新+区间查询

    LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  9. HDU 3308 LCIS (线段树区间合并)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[ ...

随机推荐

  1. jquery hover事件只触发一次动画

    最近工作时遇到个关于动画的问题,如下: $("div").hover( function() { $(this).animate({"margin-top":& ...

  2. margin的两个有趣现象:margin合并和margin塌陷

    margin合并 当两个元素在垂直方向并列,分别设置margin值时会发生一个margin合并的现象 举个例子,有两个div,垂直并列,box1设置margin-bottom:20px,box2设置m ...

  3. Ubuntu下使用终端ssh访问设置了密钥的云服务器

    首先先安装OpenSSH客户端,可以直接apt-get安装 sudo apt-get install openssh-server 然后将私钥权限修改为600 chmod 600 keyfile 最后 ...

  4. live555源码学习1---Socket流程架构图

    怎么说呢,换了工作环境,好多软件公司禁止使用了,有道笔记也无法使用了.发现博客园还可以上传图片,以后只能在这里记录了. 越发的感觉需要尽快把live555的代码拿下.因为工作环境问题,webrtc的源 ...

  5. C++ 中operator用法:隐式类型转换

    [转]C++ operator两种用法 C++,有时它的确是个耐玩的东东,就比如operator,它有两种用法,一种是operator overloading(操作符重载),一种是operator c ...

  6. contenteditable元素的placeholder输入提示语设置

    在某些情况下,textarea是不够用的,我们还需要显示一些图标或者高亮元素,这就需要用富文本编辑器,而富文本编辑器本质上是HTML元素设置了contenteditable. 然后可能需要像input ...

  7. JavaScript数据类型之文本类型

    引言 字符串(string)是一组由16位值组成的不可变的有序序列,每个字符通常来自于Unicode字符集.JavaScript通过字符串类型来表示文本.字符串的长度(length)是其所含16位值的 ...

  8. MYSQL的binlog日志

    binlog 基本认识 MySQL的二进制日志以事件形式,记录了所有的DDL和DML(除了数据查询语句)语句,及语句执行消耗时间. MySQL的二进制日志是事务安全型的,是MySQL最重要的日志. b ...

  9. C#、Java中的一些小知识点总结(持续更新......)

    前言:在项目中,有时候一些小的知识,总是容易让人忽略,但是这些功能加在项目中往往十分的有用,因此笔者在这里总结项目中遇到的一些实用的小知识点,以备用,并持续更新...... 1.禁用DataGridV ...

  10. jvm运行时内存模式

    jvm内存模型 内存模型粗略划分为:堆和栈 详细划分为:堆,虚拟机栈,方法区,本地方法区,程序计数器 程序计数器: 为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程 ...