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

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. MyBatis笔记----报错:Exception in thread "main" org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)解决方法

    报错 Exception in thread "main" org.apache.ibatis.binding.BindingException: Invalid bound st ...

  2. python 常见函数的用法

    filter(function,ls) 函数包括两个参数,分别是function和list.该函数根据function参数返回的结果是否为真来过滤list参数中的项,最后返回一个新列表. 如: map ...

  3. python列表与元组的用法

    python列表与元组的用法 目录: 列表的用法: 1.增   append   +    extend 2.删  del    remove 3.改   insert 4.查  index 5.反向 ...

  4. 【Ubuntu16.04.4】常用配置

    1.为root用户设置密码 sudo passwd root 2.设置用户登陆界面 sudo gedit  /usr/share/lightdm/lightdm.conf.d/50-unity-gre ...

  5. CentOS上安装 Docker-CE以及Docker 加速器配置

    在CentOS 7.0上安装 Docker-CE 官方源安装教程 https://docs.docker.com/install/linux/docker-ce/centos/#install-usi ...

  6. Linux 系统出现电流音解决方案

    迫于Windows 系统最近的各种故障,今天脱坑换了openSUSE Linux ,在上网途中播放视频时偶尔会出现电流音,虽然影响不大,但是还是进行了一些排查. 通过观察电流音出现时的系统负载的波段, ...

  7. Linux 忘记登录密码?破解系统登陆密码

    1.重启或者开启系统,在如下界面按e 进入救援系统: 2.在linux16 这一行末尾输入:rd.break,以rd.break 的方法重置密码 3.分别执行以下命令 mount -o remount ...

  8. IntelliJ IDEA 创建Spring+SpringMVC+mybatis+maven项目

    参考自:https://www.cnblogs.com/hackyo/p/6646051.html 第一步: 创建maven项目 输入项目名和工程id 选择maven 默认就可以了 刚开始时间比较长, ...

  9. Python 列表&元组&字典&集合

    列表(list) 有序性,可存储任意类型的值 通过偏移存取,支持索引来读取元素,第一个索引为0 ,倒数第一个索引为-1 可变性 ,支持切片.合并.删除等操作 可通过索引来向指定位置插入元素 可通过po ...

  10. 15.selenium_case02

    # 抓取今日头条的新闻链接 from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWa ...