这题 FlashHu 的优化思路值得借鉴

前置引理

  • 树中所有点到某个点的距离和中,到重心的距离和是最小的。

  • 把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上。

  • 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

  • 一棵树最多有两个重心,且相邻;同时,拥有奇数个节点的树只有一个重心

  • 其实是树的重心本身的定义:各个子树大小皆不超过总节点数的一半的节点即为树的重心(证明:不管向哪一侧移动,对应的子树节点个数都是 $\le$ 树的总节点一半的,也就是说,剩下的节点数 $\ge$ 总节点数的一半,故该点为中心)

题解

根据引理 $1$ ,题目很明显是要求维护树的重心

最简单的方法是启发式合并,根据引理 $3, 5$ ,每加一条边,若当前子树大小 $\ge$ 总节点数的一半,就将重心往这里移一次,复杂度 $O (n \log^2 n)$

对于优化,根据引理 $2$ ,取出两棵原树在新树上之间的路径,进行类似二分的操作,对于当前查询区间 $[L, p), (p, R]$ ,并同时存储 $L$ 之前的子树节点总和 $lsum$ ,以及 $R$ 之后的 $rsum$ ,且因为 $Splay$ 上是根据中序遍历,所以当前节点在 $Splay$ 上的左右子节点可以代表分割的左右区间,故若 $lsum + size[son[p][0]] \le half \&\& size[son[p][1]] + rsum \ge half$ ,那么点 $p$ 即为树的一个重心(注意由于在当前 $Splay$ 上 $lsum$ 所属的节点属于实点且已经跳过,故 $size[son[p][0]]$ 不会重复计算到 $lsum$,因为 $LCT$ 中维护的子树为 $Splay$ 中的子树,虚树的维护是一定不会将同一 $Splay$ 的实点加进去的),根据引理 $4$ ,若总节点数为奇数,那么已经可以结束查找了;反之则需继续查找是否有编号更小的,看左右区间哪边子节点多就去哪里就好了,总复杂度 $O (n \log n)$

代码

 #include <iostream>
#include <cstdio>
#include <cstring> using namespace std; const int MAXN = 1e05 + ; int father[MAXN]= {};
int son[MAXN][]= {};
int subtree[MAXN]= {}, virsize[MAXN]= {};
int revtag[MAXN]= {}; int isroot (int p) {
return son[father[p]][] != p && son[father[p]][] != p;
}
int sonbel (int p) {
return son[father[p]][] == p;
}
void reverse (int p) {
if (! p)
return ;
swap (son[p][], son[p][]);
revtag[p] ^= ;
}
void pushup (int p) {
subtree[p] = subtree[son[p][]] + subtree[son[p][]] + virsize[p] + ;
}
void pushdown (int p) {
if (revtag[p]) {
reverse (son[p][]), reverse (son[p][]);
revtag[p] = ;
}
}
void rotate (int p) {
int fa = father[p], anc = father[fa];
int s = sonbel (p);
son[fa][s] = son[p][s ^ ];
if (son[fa][s])
father[son[fa][s]] = fa;
if (! isroot (fa))
son[anc][sonbel (fa)] = p;
father[p] = anc;
son[p][s ^ ] = fa, father[fa] = p;
pushup (fa), pushup (p);
}
int Stack[MAXN];
int top = ;
void splay (int p) {
top = , Stack[++ top] = p;
for (int nd = p; ! isroot (nd); nd = father[nd])
Stack[++ top] = father[nd];
while (top > )
pushdown (Stack[top]), top --;
for (int fa = father[p]; ! isroot (p); rotate (p), fa = father[p])
if (! isroot (fa))
sonbel (p) == sonbel (fa) ? rotate (fa) : rotate (p);
}
void Access (int p) {
for (int tp = ; p; tp = p, p = father[p])
splay (p), virsize[p] += subtree[son[p][]] - subtree[tp], son[p][] = tp, pushup (p);
}
void Makeroot (int p) {
Access (p), splay (p), reverse (p);
}
void Split (int x, int y) {
Makeroot (x);
Access (y), splay (y);
}
void link (int x, int y) {
Split (x, y);
father[x] = y, virsize[y] += subtree[x];
pushup (y);
} int ances[MAXN]; // 重心用并查集维护
int find (int p) {
return p == ances[p] ? p : ances[p] = find (ances[p]);
} int N, Q;
char opt[]; int Newgrvy (int p) {
int half = subtree[p] >> ;
int isodd = subtree[p] & ;
int lsum = , rsum = ;
int grvy = N + ;
while (p) {
pushdown (p);
int l = lsum + subtree[son[p][]], r = rsum + subtree[son[p][]];
if (l <= half && r <= half) {
if (p < grvy)
grvy = p;
if (isodd)
return grvy;
}
if (l > r) {
rsum += subtree[son[p][]] + virsize[p] + ;
p = son[p][];
}
else {
lsum += subtree[son[p][]] + virsize[p] + ;
p = son[p][];
}
}
splay (grvy);
return grvy;
} int getnum () {
int num = ;
char ch = getchar (); while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return num;
} int ans = ;
int main () {
N = getnum (), Q = getnum ();
for (int i = ; i <= N; i ++)
subtree[i] = , ances[i] = i, ans ^= i;
for (int i = ; i <= Q; i ++) {
scanf ("%s", opt + );
if (opt[] == 'A') {
int x = getnum (), y = getnum ();
link (x, y);
int fx = find (x), fy = find (y);
Split (fx, fy);
int grvy = Newgrvy (fy);
ans ^= fx ^ fy ^ grvy;
ances[fx] = ances[fy] = ances[grvy] = grvy;
}
else if (opt[] == 'Q') {
int p = getnum ();
printf ("%d\n", find (p));
}
else if (opt[] == 'X')
printf ("%d\n", ans);
} return ;
} /*
10 10
Xor
Q 1
A 10 1
A 1 4
Q 4
Q 10
A 7 6
Xor
Q 7
Xor
*/

BZOJ 3510 - 首都 「 $LCT$ 动态维护树的重心」的更多相关文章

  1. BZOJ 3510: 首都 LCT + multiset维护子树信息 + 树的重心

    Code: #include<bits/stdc++.h> #define maxn 200000 #define inf 1000000000 using namespace std; ...

  2. 2019 ICPC上海网络赛 A 题 Lightning Routing I (动态维护树的直径)

    题目: 给定一棵树, 带边权. 现在有2种操作: 1.修改第i条边的权值. 2.询问u到其他一个任意点的最大距离是多少. 题解: 树的直径可以通过两次 dfs() 的方法求得.换句话说,到任意点最远的 ...

  3. BZOJ.3510.首都(LCT 启发式合并 树的重心)

    题目链接 BZOJ 洛谷 详见这. 求所有点到某个点距离和最短,即求树的重心.考虑如何动态维护. 两棵子树合并后的重心一定在两棵树的重心之间那条链上,所以在合并的时候用启发式合并,每合并一个点检查sz ...

  4. BZOJ 3510 首都 (LCT)

    洛谷P4299传送门 题目大意:给你一颗树,边是一条一条连上去的 在连接过程中会存在询问,询问当前节点所在联通块(其实是一颗树)的重心是哪个节点 以及森林中所有树的重心的异或和 在做这道题之前,要先了 ...

  5. 计蒜客D2T2 蒜头君的排序(动态维护树状数组)

    蒜头君的排序(sort) 2000ms 262144K 蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺 ...

  6. 【刷题】BZOJ 3510 首都

    Description 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从 ...

  7. 洛谷P4299 首都(BZOJ3510)(LCT,树的重心,二分查找)

    Update:原来的洛谷U21715已成坑qwq 已经被某位管理员巨佬放进公共题库啦!又可以多一个AC记录啦! 洛谷题目传送门 其实也可以到这里交啦 思路分析 动态维护树的重心 题目中说到国家的首都会 ...

  8. BZOJ 3924 ZJOI2015 幻想乡战略游戏 树链剖分

    题目链接:https://www.luogu.org/problemnew/show/P3345(bzoj权限题) 题意概述:动态维护树的上所有点到这棵树的带权重心的距离和.N,Q<=10000 ...

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

    本文转自http://fanhq666.blog.163.com/blog/static/81943426201172472943638/ 求树重心的方法:(NlogN) http://www.cnb ...

随机推荐

  1. KMP之Z-function (扩展kmp)

    http://codeforces.com/blog/entry/3107 // s[0, ..., n-1], z[0] = 0// z[i] is the length of the longes ...

  2. PHP-从零开始使用Solr搜索引擎服务(上)

    前言: 原文地址: http://www.cnblogs.com/JimmyBright/p/7156069.html 使用搜索引擎,我们常规的数据查询会快很多,还可以对关键词进行中文分词查询,返回一 ...

  3. IoT固/软件更新及开源选项

    会出什么问题呢?大多数这些设备的设计都不像是被恶意攻击的目标. 嵌入式系统传统上被认为是稳定的产品, 但实施起来成本高昂, 因为投资回报率(ROI)在的周期比较长. 在过去一旦发货, 就很少需要更新这 ...

  4. 使用SUID二进制文件进行Linux权限升级技巧

      0x00 基础知识 众所周知,在Linux中一切都以文件存在,包括具有允许或限制三个执行操作(即读/写/执行)权限的目录和设备.因此,当给任何文件设置权限时,应该需要了解允许的Linux用户或限制 ...

  5. Paxos Made Simple【翻译】

    Paxos一致性算法——分布式系统中的经典算法,论文本身也有一段有趣的故事.一致性问题是分布式系统的根本问题之一,在论文中,作者一步步的加强最初一致性问题(2.1节提出的问题)的约束条件,最终导出了一 ...

  6. golang管道

    golang中的channel channel用于goroutine之间的通信 如果不用channel,使用共享全局变量的方式,需要加锁 // synchornized 同步 // golang中的 ...

  7. 南昌邀请赛网络赛 D.Match Stick Game(dp)

    南昌邀请赛网络赛 D.Match Stick Game 题目传送门 题目就会给你一个长度为n的字符串,其中\(1<n<100\).这个字符串是一个表达式,只有加减运算符,然后输入的每一个字 ...

  8. js 读写 cookie 简单实现

    const getItem = key => { let ca = document.cookie.split('; '); for (let i = 0; i < ca.length; ...

  9. 使用Hystrix实现自动降级与依赖隔离-微服务

    转载: https://www.jianshu.com/p/138f92aa83dc Hystrix出现的原因: hystrix是netflix开源的一个容灾框架,解决当外部依赖故障时拖垮业务系统.甚 ...

  10. Python【pymysql】模块

    import pymysql# 1.连上数据库 账号.密码 ip 端口号 数据库#2.建立游标#3.执行sql#4 .获取结果# 5.关闭游标#6.连接关闭coon = pymysql.connect ...