hdu-1540线段树刷题
title: hdu-1540线段树刷题
date: 2018-10-18 19:55:21
tags:
- acm
- 刷题
categories: - ACM-线段树
概述
哇,,,这道线段树的题可以说是到目前为止我所做过的最难的一道了吧QAQ,,,,,,
一开始读完题就是一脸懵逼,,,,完全不知道该从哪里下手,,,就是知道这是一道线段树的题也不知道该怎么下手啊啊啊,,,,
最后还是看了kaungbin大佬的代码,,,QAQ
光是读代码就花了一两个小时,,,(不过也有可能和今天贼困有关,,,脑袋不怎么转啊
分析思路
题意
大概的题意就是一串在一条线上的村庄,,或者说是点,,,一开始都为1,,,然后有三种不同的操作,,,
- d a: 意味着将a这个点置为0,,,
- q a: 意味着询问a周围有多少的1,,,只要碰到零就不算了,,,例如110111110,,(q 5) = 5
- r: 意味着将上一个被置为零的点置为1
分析
我的想法
一开始我看到有需要上一次操作的情况,,就想着要将这些d操作保存下来,,适合这道题的就是栈,,,
然后就是询问了,,,我那时想着既然要求a周围这些1的个数,,那我就找到两端的0不就行了,,,然后从这里就彻底的脑抽了,,,又想着用线段树去求这段区间的和,,,,然后结果显而易见,,,,t了,,,
因为,,这种想法线段树根本没有用啊!!!!都找出那两端的0所在的位置直接减不就行了,,,这不就是裸暴力吗,,,,,哇,,,被自己蠢哭(๐॔˃̶ᗜ˂̶๐॓),,,,
斌神的做法
首先将这段线划分成多个区段,,,每个区段保存的信息有:从这去区段的左端点开始最长的连续1的个数ll 、 从这个区段右端点开始的最长的连续1的个数rl 、 还有这个区段最大的连续点的个数ml。。。
建树:ll = rl = ml = 区间长
更新:
叶子节点置一置零,,,
左右递归更新
其他区间:(pushup())
父节点.ll = 左节点.ll 父节点.rl = 右节点.rl
父节点.ml取左右节点的最大的一个ml
若左节点的rl + 右节点的ll > 父节点的ml,,,,就更新为前者
对于父节点的ll,rl
如果左节点的ll为左节点的长度,,,就说明左节点从左端点开始的连续1的最大的个数就为左节点包含的点的个数,,,所以此时的父节点的ll就要和右节点的ll合并
同理,,,父节点的rl也要进行这样的判断
查询:
对于一些特殊的区间直接返回该区间的最大的连续1的个数也就是ml
当loc在中点左时,,,就要从左节点来判断,,,判断的条件是loc是否超出了rl的最左端(画图更容易理解一些),,,超出的话就说明loc所在的连续的1一部分是在左节点的rl里另一部分是在右节点的ll里,,,就分成两个点查询,,,一个是在左节点的loc,,,另一个时在右节点的mid+1那个点
同理,,若在中点的右时也有类似的判断,,,
大体上说就是不断地判断要找到那个点相对ll,rl的位置,,,最后把递归查询到的结果合并就行了,,,
字丑见谅,,,,(不过应该没人看把,,,,
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn = 5e5 + 10;
struct node
{
int l;
int r;
int ml;
int ll;
int rl;
}node[maxn << 2];
void build(int rt , int l , int r)
{
node[rt].l = l;
node[rt].r = r;
node[rt].ml = node[rt].ll = node[rt].rl = r - l + 1; //刚开始肯定是区间的长度
if(l == r) return;
int mid = (l + r) >> 1;
build(rt << 1 , l , mid);
build(rt << 1 | 1 , mid + 1 , r);
return;
}
void update(int rt , int loc , int val)
{
if(node[rt].l == node[rt].r)
{
if(val) node[rt].ml = node[rt].ll = node[rt].rl = 1; //摧毁和重建两种
else node[rt].ml = node[rt].ll = node[rt].rl = 0;
return;
}
int mid = (node[rt].l + node[rt].r) >> 1;
if(loc <= mid) update(rt << 1 , loc , val);
else update(rt << 1 | 1 , loc , val);
//递归更新
//先更新父节点的两个,ll,rl
node[rt].ll = node[rt << 1].ll;
node[rt].rl = node[rt << 1 | 1].rl;
//然后是父节点的ml
node[rt].ml = max(node[rt << 1].ml , node[rt << 1 | 1].ml);
node[rt].ml = max(node[rt].ml , node[rt << 1].rl + node[rt << 1 | 1].ll);
//父节点的ll,rl可能就是左右节点的ll,,rl,,,,当刚好是子节点的全部时还要加上另一个区间的一部分
if(node[rt << 1].ll == node[rt << 1].r - node[rt << 1].l + 1)
node[rt].ll += node[rt << 1 | 1].ll;
if(node[rt << 1 | 1].rl == node[rt << 1 | 1].r - node[rt << 1 | 1].l + 1)
node[rt].rl += node[rt << 1].rl;
return;
}
int query(int rt , int loc)
{
//特殊情况直接返回ml
if(node[rt].l == node[rt].r || node[rt].ml == 0 || node[rt].ml == node[rt].r - node[rt].l + 1)
return node[rt].ml;
int mid = (node[rt].l + node[rt].r) >> 1;
if(loc <= mid)
{
if(loc >= node[rt << 1].r - node[rt << 1].rl + 1)
return query(rt << 1 , loc) + query(rt << 1 | 1 , mid + 1);
else
return query(rt << 1 , loc);
}
else
{
if(loc <= node[rt << 1 | 1].l + node[rt << 1 | 1].ll - 1)
return query(rt << 1 | 1 , loc) + query(rt << 1 , mid);
return query(rt << 1 | 1 , loc);
}
}
int main()
{
int n , m;
while(scanf("%d%d" , &n , &m) != EOF)
{
build(1 , 1 , n);
int q[maxn];
int toc = 0;
int t = 0;
while(m--)
{
char c;scanf(" %c" , &c);
if(c == 'D')
{
scanf("%d" , &t);
q[toc++] = t; //把摧毁操作保存
update(1 , t , 0);
}
else if(c == 'Q')
{
scanf("%d" , &t);
printf("%d\n" , query(1 , t));
}
else
{
if(t)
{
t = q[--toc];
update(1 , t , 1);
}
}
}
}
return 0;
}
//kaungbin
hdu-1540线段树刷题的更多相关文章
- hdu-5023线段树刷题
title: hdu-5023线段树刷题 date: 2018-10-18 13:32:13 tags: acm 刷题 categories: ACM-线段树 概述 这道题和上次做的那道染色问题一样, ...
- poj-2777线段树刷题
title: poj-2777线段树刷题 date: 2018-10-16 20:01:07 tags: acm 刷题 categories: ACM-线段树 概述 这道题是一道线段树的染色问题,,, ...
- zoj-1610线段树刷题
title: zoj-1610线段树刷题 date: 2018-10-16 16:49:47 tags: acm 刷题 categories: ACM-线段树 概述 这道题是一道简单的线段树区间染色问 ...
- HDU - 1540 线段树的合并
这个题题意我大概解释一下,就是一开始一条直线,上面的点全是联通的,有三种操作 1.操作D把从左往右第x个村庄摧毁,然后断开两边的联通. 2.询问Q节点相联通的最长长度 3.把最后破坏的村庄重建. 这个 ...
- hdu 1540 线段树
这题的意思是现在有一些村庄成一条直线排列,现在有三个操作,D:摧毁一个指定的村庄,Q:询问与指定村庄相连的村庄个数, 就是这个村庄向左和向右数村庄数量,遇到尽头或损坏的村庄为止,这个就是与这个村庄相连 ...
- HDU 4893 线段树裸题
Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Oth ...
- hdu 1754 线段树模板题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 #include <cstdio> #include <cmath> # ...
- HDU 1540<线段树,区间并>
题目连接 参考 题意: 维护各个点的连续的最大连续长度. 思路: 主要是维护一个区间的三个变量ll,f[i].l为起点向右的最大连续 长度,rl:f[i].r为起点向左的最大连续长度,ml:[l,r] ...
- K - Transformation HDU - 4578 线段树经典题(好题)
题意:区间 加 变成定值 乘 区间查询:和 平方和 立方和 思路:超级超级超级麻烦的一道题 设3个Lazy 标记分别为 change 改变mul乘 add加 优先度change>m ...
随机推荐
- soj1010. Zipper
1010. Zipper Constraints Time Limit: 1 secs, Memory Limit: 32 MB Description Given three strings, yo ...
- ArcGis10.1 Feature Class Properties无法编辑
被这个问题困扰了很久,最后从官网帮助中查询到资料解决了 先看一下截图: 这种问题一般是FratureClass被锁,被mxd引用,并且发布服务有实例在运行,但我新创建一个FeatureClass也无法 ...
- Ubuntu 13.04 主机名的修改
由于某些原因,要修改Ubuntu的主机名,晚上Google了一下,要改的地方为/etc/hostname,即将里面的字符串替换为你要起的主机名即可. sudo vi /etc/hostname 修改即 ...
- 【leetcode 简单】 第九十题 字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. 案例: s = "leetcode" 返回 0. s = "loveleetcod ...
- JS设计模式——8.桥接模式
桥接模式的用途 在实现API的时候,桥接模式非常有用. 在设计一个JavaScript API的时候,可以用这个模式来弱化它与使用它的类和对象之间的耦合. 示例:事件监听器 桥接模式最常见和实际的应用 ...
- 【FCS NOI2018】福建省冬摸鱼笔记 day5
第五天,也是讲课的最后一天. 数据结构专题,讲师:杨志灿 他的blog我似乎找不到了……以前肯定是在百度博客里面.但是现在百度博客消失了. PPT做的很有感觉,说了很多实用的技巧. 我觉得其实是收获最 ...
- go 函数的作用域及可见性
1.全局变量,在程序整个生命周期有效 比如: test.go 中 我们定义 了 a 作为全局变量,那么在这个程序中任何地方都可以调用a, 这个 2. 局部变量,分为两种:1)函数内定义,2)语句块内定 ...
- ip分片重组 ip_defrag
在ip_local_deliver中,如果检测到是分片包,则需要进行分片重组: ip_local_deliver |-->ip_is_fragment //判断是否为分片包 |-->ip_ ...
- Linux input子系统学习总结(三)----Input设备驱动
Input 设备驱动 ---操作硬件获取硬件寄存器中设备输入的数据,并把数据交给核心层: 一 .设备驱动的注册步骤: 1.分配一个struct input_dev : struct ...
- vs2012 连接oracle11g 及数据的insert及select 的总结
下载链接Oracle 11g所需的驱动ODTwithODAC1120320_32bit,下载链接为http://www.oracle.com/technetwork/topics/dotnet/uti ...