题目链接 BZOJ

洛谷

详见这.

求所有点到某个点距离和最短,即求树的重心。考虑如何动态维护。

两棵子树合并后的重心一定在两棵树的重心之间那条链上,所以在合并的时候用启发式合并,每合并一个点检查sz[]大的那棵子树的重心(记为root)最大子树的sz[]*2是否>n;

若>n,则向fa移动一次(先把合并点Splay到根)。重心还一定是在sz[]大的那棵子树中,且移动次数不会超过sz[]小的子树的点数(所以总移动次数不会超过O(n)?)。

复杂度 \(O(nlog^2n)\)

具体实现。。想通了真是特别简单。。也就想想简单

先Link,然后从心向右中序遍历(深度大是向右!).

每到一个点判断其右子树和虚树的sz和*2是否>当前根的sz,如果是,则当前点是要找的重心,继续下一个。若sz[x]*2<sz[root],那么root就是根了。

可能有多个重心,这些重心应该是连续的一段。因为编号最小,若sz[x] * 2==sz[root] && id[x]<id[root],则更新root继续。

用并查集维护重心。(Find_root()太慢了)

另外不需要再算左子树取max,及深度小于它的子树大小,因为既然重心在那边就不会大过另一边,更何况另一边还又插入了一棵子树。

维护子树最大值找重心好像不行吧,合并更新好像很麻烦,直接用sz[]就可以找。

注意用点的sz[]前先Splay()更新!

至于更新重心可以通过缩短左右区间 能到O(nlogn)的方法(还比这个好理解?)以后再做吧

为什么一定要Make_root()重心。。突然觉得根本不明白LCT,不知道怎么走的了。唉以后再说吧。。先水过去

//4044kb	2256ms
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=1e5+5; #define lson son[x][0]
#define rson son[x][1] int n,m,Ans,sz[N],sz_i[N],_fa[N],fa[N],son[N][2],sk[N];
bool tag[N];
inline void Update(int x){
sz[x]=sz[lson]+sz[rson]+sz_i[x]+1;
}
inline bool n_root(int x){
return son[fa[x]][0]==x||son[fa[x]][1]==x;
}
inline void Rev(int x){
std::swap(lson,rson), tag[x]^=1;
}
inline void PushDown(int x){
if(tag[x]) Rev(lson),Rev(rson),tag[x]=0;
}
void Rotate(int x)
{
int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
if(n_root(a)) son[b][son[b][1]==a]=x;
if(son[x][r]) fa[son[x][r]]=a;
fa[a]=x, fa[x]=b, son[a][l]=son[x][r], son[x][r]=a;
Update(a);
}
void Splay(int x)
{
int t=1,a=x; sk[1]=x;
while(n_root(a)) sk[++t]=a=fa[a];
while(t) PushDown(sk[t--]);
while(n_root(x))
{
a=fa[x];
if(n_root(a)) Rotate(son[a][1]==x^son[fa[a]][1]==a?x:a);
Rotate(x);
}
Update(x);
}
void Access(int x){
for(int pre=0; x; x=fa[pre=x])
Splay(x), sz_i[x]+=sz[rson]-sz[pre], rson=pre;//Update(x);
}
void Make_root(int x){
Access(x), Splay(x), Rev(x);
}
void Split(int x,int y){
Make_root(x), Access(y), Splay(y);
}
int Find_root(int x)
{
Access(x), Splay(x);
while(lson) x=lson;
return x;
}
void Link(int x,int y){
Split(x,y), sz_i[y]+=sz[x], fa[x]=y, Update(y);
}
int Get_fa(int x){
return x==_fa[x]?x:_fa[x]=Get_fa(_fa[x]);
}
//int Next(int x){//?
// PushDown(x), x=rson;
// while(lson) PushDown(x),x=lson;
// return x;
//}
int q[N],cnt;
void DFS(int x,int lim)
{
PushDown(x);
if(lson) DFS(lson,lim);
if(cnt>lim) return;
q[++cnt]=x;
if(cnt>lim) return;
if(rson) DFS(rson,lim);
}
int Union(int x,int y)
{
int r1=Get_fa(x),r2=Get_fa(y);
// int r1=Find_root(x),r2=Find_root(y);
Splay(r1), Splay(r2);//先Splay更新sz[]!
if(sz[r1]>sz[r2]||(sz[r1]==sz[r2]&&y>x)) std::swap(x,y),std::swap(r1,r2);//x->y r1->r2
int lim=sz[r1],res=r2,tot=sz[r2]+sz[r1];
Link(x,y), Access(x), Splay(r2);
cnt=0;
DFS(r2,lim);
for(int sum,i=1; i<=cnt; ++i)
{
Splay(q[i]), sum=sz[son[q[i]][1]]+sz_i[q[i]]+1;
// if(tot<sum<<1) res=q[i];//不这么写是不是有点问题啊
// else if(tot==sum<<1) res=std::min(res,q[i]);
if(tot<sum<<1||(tot==sum<<1&&q[i]<=res)) res=q[i];
else break;
}
Make_root(res);
_fa[r1]=_fa[r2]=_fa[res]=res/*更新重心*/;
return res;
}
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
} int main()
{
n=read(),m=read();
int res=0,x,y; char opt[5];
for(int i=1; i<=n; ++i) _fa[i]=i, res^=i, sz[i]=1;
while(m--)
{
scanf("%s",opt);
if(opt[0]=='A') x=read(),y=read(),res^=Get_fa(x)^Get_fa(y)^Union(x,y);
else if(opt[0]=='Q') x=read(),printf("%d\n",Get_fa(x));//printf("%d\n",Find_root(x));
else printf("%d\n",res);
}
return 0;
}

BZOJ.3510.首都(LCT 启发式合并 树的重心)的更多相关文章

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

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

  2. BZOJ 3510 首都 (LCT)

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

  3. [BZOJ4530][Bjoi2014]大融合 LCT + 启发式合并

    [BZOJ4530][Bjoi2014]大融合 试题描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是 ...

  4. BZOJ 3510 - 首都 「 $LCT$ 动态维护树的重心」

    这题 FlashHu 的优化思路值得借鉴 前置引理 树中所有点到某个点的距离和中,到重心的距离和是最小的. 把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上. 一棵树 ...

  5. BZOJ 3123 [SDOI2013] 森林 - 启发式合并 主席树

    Description 给你一片森林, 支持两个操作: 查询$x$到$y$的$K$大值,  连接两棵树中的两个点 Solution 对每个节点$x$动态开权值线段树, 表示从$x$到根节点路径上权值出 ...

  6. BZOJ2888 资源运输(LCT启发式合并)

    这道题目太神啦! 我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去.那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器--启发式 ...

  7. 【BZOJ-2888】资源运输 LCT + 启发式合并

    2888: 资源运输 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 63  Solved: 33[Submit][Status][Discuss] D ...

  8. 【刷题】BZOJ 3510 首都

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

  9. 4.17 省选模拟赛 远行 LCT 启发式合并 倍增

    容易写出nQ的暴力 由于数据是期望的时间 所以直接dfs可以跑的很快 可以拿到70分. 当然 可以进一步优化暴力 使用换根dp 然后可以将暴力优化到n^2. const int MAXN=300010 ...

随机推荐

  1. 【转】Linux中常见问题(磁盘 定时任务)

    [转]Linux中常见问题(磁盘 定时任务) 第1章 linux无法上网 1)     第一步,先ping域名. ping www.baidu.com 2)再ping一个公网ip , ping 223 ...

  2. Word打开默认显示缩略图,而不是文档结构图

    So easy! 1.打开Word文档,点击缩略图右侧的"X",关闭缩略图: 2.打开菜单[视图],勾选"文档结构图": 3.关闭当前Word文档: 4.再次打 ...

  3. uboot中的快捷菜单的制作说明 【转】

    转自:http://blog.chinaunix.net/uid-22030783-id-366971.html   在uboot中加入快捷操作菜单的方法非常简单,在论坛发布的uboot201003V ...

  4. mysql安装与卸载(阿里云)

    1.安装rpm包rpm -Uvh http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm 2.安装mysqlyum -y i ...

  5. (网络编程)基于tcp(粘包问题) udp协议的套接字通信

    import   socket 1.通信套接字(1人1句)服务端和1个客户端 2.通信循环(1人多句)服务端和1个客户端 3.通信循环(多人(串行)多句)多个客户端(服务端服务死:1个客户端---&g ...

  6. nginx实现tomcat的负载均衡及企业内部应用的代理

    192.168.3.87  mysql+redis+tomcat 192.168.3.112 nginx:6688 192.168.3.86  nginx+h5 环境准备 a.安装包  open jd ...

  7. idea开发swing(二)

    闲话少说,书接idea开发swing(一). 程序编译完成后,需要打包发布,如果有fat_jar的同学可以通过该插件打包,这里是使用ant来打包,步骤如下: 一.编写build.xml <?xm ...

  8. JAVA Random 随机类

    nextInt 方法 得到一个随机整数, 可以指定范围 package object; import static net.util.Print.*; import java.util.Random; ...

  9. python 全栈开发,Day134(爬虫系列之第1章-requests模块)

    一.爬虫系列之第1章-requests模块 爬虫简介 概述 近年来,随着网络应用的逐渐扩展和深入,如何高效的获取网上数据成为了无数公司和个人的追求,在大数据时代,谁掌握了更多的数据,谁就可以获得更高的 ...

  10. 【C++ Primer 第15章】定义派生类析构函数

    学习资料 • 基类和派生类析构函数执行顺序 定义派生类析构函数 [注意]定义一个对象时先调用基类的构造函数.然后调用派生类的构造函数:析构的时候恰好相反:先调用派生类的析构函数.然后调用基类的析构函数 ...