BZOJ 3510 - 首都 「 $LCT$ 动态维护树的重心」
这题 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$ 动态维护树的重心」的更多相关文章
- BZOJ 3510: 首都 LCT + multiset维护子树信息 + 树的重心
Code: #include<bits/stdc++.h> #define maxn 200000 #define inf 1000000000 using namespace std; ...
- 2019 ICPC上海网络赛 A 题 Lightning Routing I (动态维护树的直径)
题目: 给定一棵树, 带边权. 现在有2种操作: 1.修改第i条边的权值. 2.询问u到其他一个任意点的最大距离是多少. 题解: 树的直径可以通过两次 dfs() 的方法求得.换句话说,到任意点最远的 ...
- BZOJ.3510.首都(LCT 启发式合并 树的重心)
题目链接 BZOJ 洛谷 详见这. 求所有点到某个点距离和最短,即求树的重心.考虑如何动态维护. 两棵子树合并后的重心一定在两棵树的重心之间那条链上,所以在合并的时候用启发式合并,每合并一个点检查sz ...
- BZOJ 3510 首都 (LCT)
洛谷P4299传送门 题目大意:给你一颗树,边是一条一条连上去的 在连接过程中会存在询问,询问当前节点所在联通块(其实是一颗树)的重心是哪个节点 以及森林中所有树的重心的异或和 在做这道题之前,要先了 ...
- 计蒜客D2T2 蒜头君的排序(动态维护树状数组)
蒜头君的排序(sort) 2000ms 262144K 蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺 ...
- 【刷题】BZOJ 3510 首都
Description 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从 ...
- 洛谷P4299 首都(BZOJ3510)(LCT,树的重心,二分查找)
Update:原来的洛谷U21715已成坑qwq 已经被某位管理员巨佬放进公共题库啦!又可以多一个AC记录啦! 洛谷题目传送门 其实也可以到这里交啦 思路分析 动态维护树的重心 题目中说到国家的首都会 ...
- BZOJ 3924 ZJOI2015 幻想乡战略游戏 树链剖分
题目链接:https://www.luogu.org/problemnew/show/P3345(bzoj权限题) 题意概述:动态维护树的上所有点到这棵树的带权重心的距离和.N,Q<=10000 ...
- ACM-树重心的性质及动态维护
本文转自http://fanhq666.blog.163.com/blog/static/81943426201172472943638/ 求树重心的方法:(NlogN) http://www.cnb ...
随机推荐
- python selenium判断元素是否存在的问题
爬虫的时候经常用到这个,找到了一个比较好用的方法 原文链接:http://blog.csdn.net/u012189659/article/details/36391837 背景:selenium+p ...
- The Bells are Ringing UVALive - 4060(枚举求解)
输出整数N,使得 t1 <= N 统计有多少组t1,t2,t3,满足:1<t1<t2<t3<=1000000,t3-t1<=25,且t1,t2,t3的最小公倍数 ...
- Finding LCM LightOJ - 1215 (水题)
这题和这题一样......只不过多了个数... Finding LCM LightOJ - 1215 https://www.cnblogs.com/WTSRUVF/p/9316412.html #i ...
- HDU.2147 kiki's game (博弈论 PN分析)
HDU.2147 kiki's game (博弈论 PN分析) 题意分析 简单的PN分析 博弈论快速入门 代码总览 #include <bits/stdc++.h> using names ...
- 关于idea中新建web项目 webapp文件夹没有小蓝点 ,启动服务,访问不到解决方案
第一步: 选中项目按F4键,找到你的项目. 第二步: 选中项目下的web,如果没有web点击左上角的加号,找到web最下面,添加进去 第三步: 点开type下的节点,出来弹框, 第四步: 点击弹框的选 ...
- 4.tar的各个参数详解
转于:https://blog.csdn.net/liuyundemhsg/article/details/52525028 参数:-c :建立一个压缩文件的参数指令(create 的意思):-x : ...
- ElasticStack系列之十 & 生产中的问题与解决方案
1. 由 gc 引起节点异常 问题: 因为 gc 时会使 jvm 停止工作,如果某个节点 gc 时间过长,master ping 3次(zen discovery默认 ping 失败重试 3 次)不通 ...
- python 中的multiprocessing 模块
multiprocessing.Pipe([duplex]) 返回2个连接对象(conn1, conn2),代表管道的两端,默认是双向通信.如果duplex=False,conn1只能用来接收消息,c ...
- js判断当前浏览器是pc端还是移动端
根据用户的访问设备的不同来显示不同的页面样式,主要是判断移动设备还是电脑浏览器访问的. 下面给出js判断处理代码,以作参考. <script type="text/javascript ...
- StringBuffer类和String 类的 equals 和 ==
注意: equals(Object obj)equals方法的参数是任意对象 Object类的equals方法就是用==判断的,即判断两个对象是否为同一个对象 StringBuffer类没有重写equ ...