ACM-树重心的性质及动态维护

本文转自http://fanhq666.blog.163.com/blog/static/81943426201172472943638/

求树重心的方法:(NlogN)

http://www.cnblogs.com/qlky/p/5780933.html

还记得曾经提到过的树的“重心”吗?重心的定义是:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

树的重心的一个的性质:

树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。

这也是“道路修建”带来的启发。(证明:调整法)

树的重心的另一个性质:

把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。

这个让“重心”名副其实了。。。(证明:。。。自己好好思考一下吧。。。)

还有一个性质:

把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。

(证明:提示:张放想都没想说,要不然那两个不等式就矛盾了)

嗯,不错,有这么多性质,可以出不少恶心题了。。。

不过,我还是更关心一个事情:重心的动态维护。

如何动态呢?

情景1:添加一片叶子

根据已有性质,添加一片叶子之后新的重心要么不动要么向那片叶子的方向移动一下。这样,可以用一个link-cut tree来维护。

我们以重心为根建立这个动态树。每个节点维护它所在的子树的大小。添加叶子等于向一条路径上的维护值增加1,这个可以通过打标记实现。发现不得不移动的时候进行一次换根的操作。因为只可能移动1,所以换根的操作是可以完成的。

我们甚至还可以维护所有点到重心的距离和!这个只需给每个点加一个维护值:这个点为根的子树中所有点到这个点的距离和,通过稍微有点复杂的标记系统还是可以维护的。

情景2:删除一片叶子

只有删除操作?那么离线改成添加吧。。。

不允许离线?那么我们要换一个思路:

定义稍微广义的树的重心:每个点有一个非负权,每个边有个正的长度,到所有点的权乘以距离的和最小的点定义为重心。

注意:树的重心的位置和边的长度没有关系!。

在只有权值修改的情况下,我们可以利用树分治配合基本数据结构来维护树的重心:

注意到,我们可以维护一个子树内的点的权值和(利用dfs序)。这样给定一条边,我们就能够知道树的重心在这条边的哪边(看看哪边权值和大就行了)。

这样,我们可以先找一个比较靠近中心的边,问问应该向哪边走,再分治下去,就像树分治那样(类似二分查找?)。

当然,要想一下其他的技巧来对付”星型数据“,这个应该不难(通过拆点、拆边的技巧)。

利用这个广义一点的重心,我们发现,删除操作其实就是把权修改成0而已,可以在log^2N的时间内动态维护了。

如何处理多重心的情况?”抖动“一下权值使得只有一个重心不就行了。。。那另一个重心在哪里?这个只是个细节问题。。。

能否维护距离和?能否在logN的时间内维护?欢迎讨论(将子树和查询与树分治结合起来?。。。)。

情景3:移动一片叶子

把一个叶子移动到另一个地方。

这个怎么维护呢?其实,我们发现,新的重心应该在原来的重心和叶子新的位置的连线上(证明?应该是对的吧),移动距离很小。于是,也就可以维护了。

情景4:移动一个子树(被移动的子树小于原树的一半,并且保持它的根不变)

这个可以维护吗?

新的重心在原来重心和新子树的接合点的连线上吗?(证出来的欢迎留言)

有一个可以和link-cut tree配合使用的工具,叫做Euler-tour tree,它维护树的欧拉回路,基本元素是边。利用它,可以方便的完成子树的移动,并且给定一条有向边,可以回答这条边指向的子树的大小(Eurler-tour tree中没有”根“以及”父亲“这个概念!)。如果上面的那个论断是对的,那么这个应该可以维护了(复杂度?log^2N吧。。。)

另一个思路是树块划分,把树划分成若干联通块,并且在查询的时候合并相邻的联通块使得每个联通块的大小都是sqrt(N)的级别。这个东西对维护是否有帮助?欢迎交流。。。

情景5:开始N个一个点的树,每次用一条边合并两个树,要求回答新的树的重心

离线?在线?logN?log^2N?sqrt(N)?

等待你去探索

情景6:未来的某一天,某省省选出了一个叫做”疯狂的重心“的数据结构题,时限1分钟,内存若干G,标程几十K,当场无人做。。。。。。

时间: 2024-10-16 19:55:04

ACM-树重心的性质及动态维护的相关文章

BZOJ 1901 Zju 2112 Dynamic Rankings 动态维护第k小 树套树

题目大意:动态维护第k小. 思路:线段树套treap,裸题,就是不怎么好写. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 50010 #define INF 1e9 #define LEFT (pos << 1) #define RIGHT (pos << 1|1) #define SIZ

【bzoj4184】shallot 线段树+高斯消元动态维护线性基

题目描述 小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏. 每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且 让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大. 这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗? 你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0. 输入 第一行一个正整数n表示总时间:第二行n个整数a1,a2...an,

[xyz模拟题]动态维护树的直径

专出神题的xyz. 支持删加边.修改点权.维护树的直径. LCT 需要额外记录子树信息.用一个堆维护. #include<cstdio> #include<cstring> #include<algorithm> #include<set> #include<vector> #include<queue> using namespace std; #define rep(i,x,y) for(i=x;i<=y;i++) #def

主席树初探 &amp; bzoj 3295: [Cqoi2011] 动态逆序对 题解

[原题] 3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 778  Solved: 263 [Submit][Status] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元

uva11078 - Open Credit System(动态维护关键值)

这道题使用暴力解法O(n*n)会超时,那么用动态维护最大值可以优化到O(n).这种思想非常实用. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<map> #include<set> #include<vector> #i

hdu 4081 Qin Shi Huang&#39;s National Road System 树的基本性质 or 次小生成树

During the Warring States Period of ancient China(476 BC to 221 BC), there were seven kingdoms in China ---- they were Qi, Chu, Yan, Han, Zhao, Wei and Qin. Ying Zheng was the king of the kingdom Qin. Through 9 years of wars, he finally conquered all

【BZOJ-2460&amp;3105】元素&amp;新Nim游戏 动态维护线性基 + 贪心

3105: [cqoi2013]新Nim游戏 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 839  Solved: 490[Submit][Status][Discuss] Description 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿.拿走最后一根火柴的游戏者胜利. 本题的游戏

bzoj 2300 动态维护上凸壳(不支持删除)

新技能GET. 用set保存点,然后只需要找前趋和后继就可以动态维护了. 1 /************************************************************** 2 Problem: 2300 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:556 ms 7 Memory:4824 kb 8 ************************************************

【POJ3764】The xor-longest Path Trie树+异或性质

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43486733"); } 题意: 多组数据. 给你一颗树, 然后求一条最长异或路径, 异或路径长度定义为两点间简单路径上所有边权的异或和. 题解: 首先无根树转有根树再在树上跑一遍算出每个点到根的异或和. 然后两点间异或路径长度就是a[i]*