题目链接 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. Python中的exec、eval使用实例

    Python中的exec.eval使用实例 这篇文章主要介绍了Python中的exec.eval使用实例,本文以简洁的方式总结了Python中的exec.eval作用,并给出实例,需要的朋友可以参考下 ...

  2. CentOS配置SSH无密码

    210-211/212/213的集群中,新增215节点,操作:1.将210的id_rsa.pub拷贝到215中:scp ~/.ssh/id_rsa.pub 192.168.0.215@host:/ho ...

  3. [转] bss段、data段、text段

    1.前言 一个程序本质上都是由 BSS 段.DATA段.TEXT段三个组成的. 本文主要分编译时和运行时分别对 对data段 bss段 text段 堆 栈作一简要说明 2. 程序编译时概念说明 程序与 ...

  4. nodejs async waterfull 小白向

    async.waterfall([function(callback){var a=3+5;callback(null,a);},function(n,callback) { callback(nul ...

  5. centos命令行变成了-bash-4.1$的解决办法

    在使用root用户是先建立了用户yunva的家目录,然后添加yunva用户,切换到yunva用户后发现 命令行变成了-bash-4.1$ ①方法一(临时生效,不推荐) 1. bashrc 在当前目录下 ...

  6. lvs基本概念、调度方法、ipvsadm命令及nat模型示例

    LVS类型: NAT:-->(DNAT) DR TUN FULLNAT LVS的常见名词解释 CIP<-->VIP--DIP<-->RIP Direct Routing: ...

  7. php ajax返回无故刷新页面

    1 前言 一个php页面,里面两个$.POST请求,一个会刷新页面,一个不会,然后就拉出来研究一下了,仅作为记录使用. 2 代码 HTML代码: <input value="查找&qu ...

  8. winform(记事本--保存和退出)

  9. h5新API之WebStorage解决页面数据通信问题

    localStorage相信大家都不陌生,今天我们要讨论的不是怎么存储数据,获取数据.而是看看WebStorage的一些妙用,相信大家在开发中遇到过这样一个场景,一个页面中嵌套一个iframe,ifr ...

  10. Linux C 结构体初始化三种形式

    最近看linux代码时发现了结构体 struct 一种新的初始化方式,各方查找对比后总结如下: 1. 顺序初始化教科书上讲C语言结构体初始化是按照顺序方式来讲的,没有涉及到乱序的方式.顺序初始化str ...