线段树小结

转自:http://www.cnblogs.com/ronaflx/archive/2011/03/14/1984147.html

按照http://www.notonlysuccess.com/牛的博客写了几道题,刷了一周效果不错
1.复习巩固了以前的知识

2.还有改正了一些写线段树毛病

3.改正了在弱数据的影响下的错误方法AC的题目

4.还学到了线段树新的类型题

总体效果不错,总结一下

HDU 1166 赤裸裸的线段树,什么都不用考虑,新手练习用
HDU 1754 依然赤裸裸……一样是单点的操作,求和改为最值,新手练习专用
HDU 1698 基础的区间操作,我每个节点开了以delta域一个sum域,然后1次查询,注意要有延迟操作
HDU 1394 由于序列的特殊性只有0 - n - 1 切每个出现1次,所以每次把头的数移到尾部 都有 ans = ans  - a[i] + (n - a[i] - 1);
    树状数组比线段树    更容易
POJ 2777 很早之前做的了,忘记了,我最初的线段数之一
POJ 3468 注意用long long 区间加减一个操作把 HDU2698 =换成 +=就OK了
PKU 2528 离散化+线段数 貌似数据比较弱,我的错误的线段树也过了
正解 改为区间的模型 右端点+1,表示端点
HDU 3016 开始读错题了……认为就是一个线段数覆盖的模型呢……然后看错了数据范围,开始按照PKU 2528的正解离散化……后来发现不用离散化果断过了,我的第一到线段树DP,就这么悲剧了
由于只能从左或者右端点下来,所以每个点可以到2个板。然后用线段树建dag图就是一个水的拓扑序DP了
poj 1436 把由于只有整数的区间,所以吧每个单位区间也当作一个点对于a, b的线段 插入 a * 2 到 b * 2 ,解决了相交于端点的问题,很巧妙的转换!!!向大牛学习了~
HDU 2795 一个水题,插入到叶子节点的简单模型,但是开始被我当作一个覆盖的统计模型了,SB了……
PKU 3667 (新学的内容)同时1A掉HOJ2687 还有 HDU 2871也是一个模型的题,让我抑郁的是那个get的实现我本来是打算在线段树中再加3个域,结果这么写太恶心了,然后看一下别人的程序,竟然用一个vector就可以过,感觉数据是不够猥琐呀,其中用了vector的insert和erase 我一直认为这两个函数是巨慢无比的,但是这题的效果还是非常好的 而且发现自己还有不好的习惯,一旦遇到做不动的题就不想写了……
以PKU 3667为例说一下这类题
一个经典的线段树,以前也见过这里类型,思考过但是一直没有做,这次找了一个这样的题A掉了
不过开始脑残了……贡献了几个WA
区间覆盖操作,找满足一定连续长度的最左边的区间
区间属性
int lcnt[N], rcnt[N], maxn[N],chk[N];
lcnt[N] 表示该区间总左边去最多能去多少,rcnt[N]同理是从右边。
chk[N]表示是否被覆盖过。-1表示不合法状态要查询子区间。
manx[N]表示该区间最大的连续长度是多少。
int all(int idx)//函数计算区间的最大长度
pass(int idx)//函数在insert或者query子区间是,继承区间的属性,更改区间的3个值
change(int idx,int d) 当区间的chk值发生改变时,计算重新计算3个值
显然当d==0时//表示该区间没有人住。3个值都为all(idx)
d== 1时为0
update(int idx)函数是最重要的操作,是每次insert回溯时改变父亲区间的属性。是这个线段树的核心
lcnt[idx] = lcnt[LEFT(idx)] + (lcnt[LEFT(idx)] == all(LEFT(idx)) ? lcnt[RIGHT(idx)] : 0);
rcnt[idx] = rcnt[RIGHT(idx)] + (rcnt[RIGHT(idx)] == all(RIGHT(idx)) ? rcnt[LEFT(idx)] : 0);
这个两句比较直观好理解。
maxn[idx] = max(max(maxn[LEFT(idx)], maxn[RIGHT(idx)]), rcnt[LEFT(idx)] + lcnt[RIGHT(idx)]);的计算是直观重要的
不用再算 maxn[idx] = (max(lcnt[idx], rcnt[idx]),maxn[idx])。原因如下
假如左儿子区间不是all的时候lcnt[idx] <= maxn[LEFT(idx)];右儿子区间同理。
而满的时候有lcnt[idx] == rcnt[LEFT(idx)] + lcnt[RIGHT(idx)];右儿子区间同理。
这个性质大大的化简的线段树的讨论过程。使得query时更加方便。
开始的时候由于maxn[idx]算错了,所以一直WA。
max(maxn[LEFT(idx)], maxn[RIGHT(idx)])这两个maxn被我写成了lcnt和rcnt
完全的代码如下,效率有点低,代码还是很直接美观的。

POJ 3667

POJ 2892 最直观的是平衡树,用树状数组的findK的功能更酷一些,线段树找第K小也可以,据说有把线段树建和平衡树的一样的方法做?有待研究

POJ 2892

此外HOJ 2910 一个线段树DP卡了……而且我无耻的利用了admin的身份看了一下数据,shit……就错了一组数据差1

3月19日更新:

线段树+扫描线的好题 HOJ 2723 POJ 也有原题stars in your window
以每个点位中心建立一个w-eps ,h - eps的矩形,然后求最大的面积带权并
可以证明在这样一个矩形中所有的点,是能包含该点覆盖点最多的矩形,是一个很重要的思想。
然后就是区间最值的问题了,开始的时候还写错了,丢人呀……

HOJ 1400
赤裸裸的线段树DP,但是由于我的英语一直没懂
简单的说一定要从1个开始连续的覆盖才可以。
还有就是插入的时候插入点就可以了,不许要插入区间。
我们可以考察两个线段,如果没有交集的话,那么前面的一条的任何一个子区间都不会被利用到。
如果有交集的话,他被利用的子区间必定包含右端点区间。

HOJ 1400

顺便HOJ 2920

HOJ 2920

树状数组,线段树同样可以解,不过这个树状数组求第K大真的很酷,所以又一次上树状数组了

hdu1255 覆盖的面积
我的极其糟糕的写法基本上退化成数了……正解是扫描线+记录被覆盖两次的线段长。查询是O(1)的,而我的方法可能退化成O(nlogn)
代码就不现丑了

POJ 2828 比较直观的算法是二分+树状数组,但是这题确实线段树代码更帅一下!!
基于这样的考虑,最后一个人到的位置一定是队列的确定位置,那么我们把这个人的位置拿掉以后,剩下的队列就是前n - 1个人的位置,那么倒数第二个人同理,取得其在当前队列中的位置。所以只要从后到前确认每个人的位置就可以了!所以此题只要一个query就可以了

query代码如下:

  int query(int p, int x)
        {
            cnt[x]--;
            if(left[x] == right[x])
                return left[x];
            if(cnt[LEFT(x)] >= p)
                return query(p, LEFT(x));
                    else
                return query(p - cnt[LEFT(x)], RIGHT(x));
        }

pku2886 Who Gets the Most Candies?(AC)

残留题目

pku3225 Help with Intervals

pku2464 Brownie Points II

pku3145 Harmony Forever

pku2991 Crane

hdu1823 Luck and Love

六道做不下去了,以后再说吧。

自己的效率实在是太低了……线段树,放一下吧,换AC自动机

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值