题目链接 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. spring+activemq配置文件内容及实现原理

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. spring整合strus2的Hellowworld

    比较笨,看了三遍才能理解敲对并正确运行: step: 1.建立web工程( Dynamic Web project)一定要勾上创建web.xml 2.导入jar包 这个就比较坑了,我查了有半个小时才查 ...

  3. 浏览器桌面通知Notification实践

    一言不合就上图: 最近常常在浏览器看到这样的消息推送,还有QQ.com的推送,现在我对这个不了解,不知道叫消息自动推送对不对,这个时chrome浏览器的截图,出现在右下角,其他浏览器的样式可能有些微差 ...

  4. Eclipse开发环境配置

    1. java环境 安装 本系统使用java6开发,老师使用1.6.0 _45版本开发,如下图所示: "开发工具"目录提供了1.6.0 _45版本32位和64位两个安装程序,大家根 ...

  5. 多CPU,多核,多进程,多线程

    当面临这些问题的时候,有两个关键词无法绕开,那就是并行和并发. 首先,要先了解几个概念: 1.进程是程序的一次执行. 2.进程是资源分配的基本单位(调度单位). 3.一个进程可以包括多个线程. 4.在 ...

  6. P2163 【[SHOI2007]园丁的烦恼】

    其实是不用把一个询问拆成四个的 把询问转化为数学语言: 对于每个查询,询问满足$a<=x<=b$且$c<=y<=d$的点$x,y$的个数 ~~自然~~想到偏序问题,看到有两个式 ...

  7. 性能测试二十四:环境部署之Redis多实例部署

    由于redis服务端是单线程实现的,因此只能占用CPU的单核,为了充分利用CPU资源,可以在一台服务器上同时启动多个redis-server实例 首先删除之前的rdb.aof文件 注释掉3个save ...

  8. Hibernate之关联关系映射(一对多和多对一映射,多对多映射)

    ~~~接着之前的Hibernate框架接着学习(上篇面试过后发现真的需要学习一下框架了,不然又被忽悠让去培训.)~~~ 1:Hibernate的关联映射,存在一对多和多对一映射,多对多映射: 1.1: ...

  9. spring配置redis注解缓存

    前几天在spring整合Redis的时候使用了手动的方式,也就是可以手动的向redis添加缓存与清除缓存,参考:http://www.cnblogs.com/qlqwjy/p/8562703.html ...

  10. HDU5730

    cdq分治+FFT 转移:dp[i]=Σdp[i-j]*a[j](1<=j<=i)