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

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. [20181015]为什么是3秒.txt

    [20181015]为什么是3秒.txt --//以前测试:连接http://blog.itpub.net/267265/viewspace-2144765/=>为什么是12秒.txt.--// ...

  2. 数据库之mysql篇(4)—— navicat操作mysql

    navicat 1.简介: navicat是一个软件,旗下针对不同数据库有不同的软件版本,支持以下数据库,还是挺厉害的: 这里我采用navicat for mysql版本.实现图形化的操作mysql, ...

  3. 自动化测试基础篇--Selenium单选框(Radio)复选框(CheckBox)

    摘自:https://www.cnblogs.com/sanzangTst/p/7686602.html 一.什么是单选框.复选框? 二.单选框:radio 三.复选框:checkbox 四.判断是否 ...

  4. SQL Server @@ERROR的小误区大Bug

    在公司项目中看到有这样使用事务的: -- 开启事务 BEGIN TRAN ) ) BEGIN ROLLBACK TRAN END COMMIT TRAN 乍一看没啥问题,仔细思考就能发现有很大的问题. ...

  5. AspNet MVC中使用Hangfire执行定时任务

    Hangfire在Aspnet中执行定时任务: 第一步: NuGet中加入Hangfire包 第二步: 添加Owin的自启动 第三步.Hangfire的后台控制仪表盘默认情况下只能本地访问,外网访问需 ...

  6. C# -- 使用System.Environment获取电脑的相关属性

    使用System.Environment获取电脑的相关属性 1.使用System.Environment获取电脑的相关属性(入门案例) static void Main(string[] args) ...

  7. 第10章 嵌入式Linux 的调试技术

    10.1  打印内核调试信息:printk printk位函数运行在内核空间, printf函数运行在用户空间.也就是说,像Linux 驱动这样的Linux内核程序只能使用printk函数输出调试信息 ...

  8. Spring AOP 术语

  9. orcFile split和读数据原理总结(hive0.13)

    http://blog.csdn.net/zhaorongsheng/article/details/72903431 官网关于orcfile的介绍 背景 Hive的rcfile格式已经使用多年,但是 ...

  10. 微软刚发布的区块链去中心化身份识别系统DID

    https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653549389&idx=1&sn=158f9feb10d76a ...