zoukankan      html  css  js  c++  java
  • 【百度之星2014~复赛 解题报告~正解】The Query on the Tree

    声明

       笔者最近意外的发现 笔者的个人网站 http://tiankonguse.com/ 的很多文章被其它网站转载,但是转载时未声明文章来源或参考自 http://tiankonguse.com/ 网站,因此,笔者添加此条声明。

        郑重声明:这篇记录《【百度之星2014~复赛 解题报告~正解】The Query on the Tree》转载自 http://tiankonguse.com/的这条记录:http://tiankonguse.com/record/record.php?id=674

    前言

    昨天写了 The Query on the Tree 的解题报告,但是遗留下一个问题,不能算是完美解决这道题.

    因为如果精心构造数据的话,昨天的题解还是会被卡住的.

    今天中午睡觉的时候突然想起一个不会被卡住的方法.

    但是由于早上玩了一会类似与宠物消消的弱智游戏,于是怎么也停不下来了.

    一个下午的时光也浪费在了这个弱智游戏上.

    到了晚上,手机终于没电了,于是来写写这道题的完美解决方法.

    这样无论怎么构造数据,tiankonguse都不用担心程序超时了.

    正文

    题意

      有一棵树,树的每个点有点权,每次有三种操作:
      1. Query x 表示查询以x为根的子树的权值和。
      2. Change x y 表示把x点的权值改为y(0<=y<=100)。
      3. Root x 表示把x变为根。
      现在度度熊想请更聪明的你帮助解决这个问题。

    背景

    这篇记录和昨天那一篇紧密相连,建议看看那个记录.

    传送门http://tiankonguse.com/record/record.php?id=673

     

    背景简述

    对于这道题,首先需要对树按1为根优先编号.

    编号的时候记录子树的权值和以及子树的编号范围.

    这样设置根的一般复杂度是O(1), 修改的一般复杂度是O( log( n ) ), 查询的一般复杂度也是 O( log( n ) ).

    修改的最坏复杂度是O( n ), 我们可以使用线段树来优化到O( log( n ) ).

    对于查询分了三部分,其中有一部分最坏情况下复杂度也是 O( n ).

    当时往二分优化上想了,但是目前的信息不满足二分的条件,所以二分不了.

    二分优化查询

    假设目前查询的是x, root 是根, y 是x的某个儿子, root 在 y 的那个子树上.

    问题1:我们要二分搜索什么?

    我们要搜索y这个节点.

    问题2:搜索的序列有递增或递减的特增吗?

    我们要搜的区间是 left[x]到 left[root], 其中 left[x] 最小,left[y] 不能确定在那个地方,也不知道 left[y] 的值.

    问题三:有人说可以使用欧拉序列加树状数组做这道题,是吗?

    欧拉序列是什么呢?

    原来欧拉序列也对树dfs编号了,只不过进入每个儿子的时候都对当前子树根编号,最后结束时再遍一次号,储存的信息貌似很丰富.

    问题四:如果我们也使用欧拉序列或者欧拉序列的思想,可以二分吗?

    貌似可以.

    因为这时x的每个儿子前面一定有一个编号是x.

    而我们需要的是 root 前面的第一个 x.

    又由于 x 是区间内最小值,所以通过二分这个最小值就可以搜到 y 了.

    问题五:最坏复杂度怎么呀?

    由于需要二分,所以最少是 O( log( n ) ).

    每次都需要判断,所以这个我们需要通过线段树来优化,可以优化到 log( n ).

    这样综合复杂度就是 O( log( n ) ^ 2 )

    问题六:那你能实现吗?

    这个当然可以,就是一个二分加线段树.

    总结

    针对昨天遗留下的问题,这里简单的总结一下解决方法.

    遗留的问题是查询的时候,如果root是查询节点x的子孙时,我们需要找到x的某个儿子y,这个儿子y还是root的祖先.

    这个查找过程用昨天的方法最坏复杂度是O( n ) 的.

    这里我找到一个方法:对树dfs编号的时候,每次在儿子前面都添加一个根节点,即把用根节点把各个儿子为根的子树分开.

    这样我们就可以使用二分查找x的儿子y了.

    因为root前面第一个编号为x的节点和y前面第一个编号为x的节点是相同的.

    而且第一个编号为x的节点的下一个节点就是y节点.

    由于x还是整个区间的最小值,所以我们就可以通过二分区间最小值来找到root前面的第一个编号为x的节点了.

    代码

    二分优化查询(其他的暴力的代码)https://github.com/tiankonguse/ACM/blob/master/astar/2014/3/2.3.cpp

    完整版的代码(两个线段树写为一个了):https://github.com/tiankonguse/ACM/blob/master/astar/2014/3/2.4.cpp

    参考

  • 相关阅读:
    程序员修神之路--容器技术为什么会这么流行
    程序员修神之路--kubernetes是微服务发展的必然产物
    程序员修神之路--有状态的服务其实可以做更多的事情
    程序员修神之路--要想做好微服务架构,并非易事!
    程序员修神之路--为什么有了SOA,我们还用微服务?
    程序员过关斩将--数据库的乐观锁和悲观锁并非真实的锁
    程序员修神之路--设计一套RPC框架并非易事
    计算机的诞生和简史
    记一次Linux修改MySQL配置不生效的问题
    为什么大多数公司都不重视技术?
  • 原文地址:https://www.cnblogs.com/tiankonguse/p/3766440.html
Copyright © 2011-2022 走看看