洛谷P4299 首都(BZOJ3510)(LCT,树的重心,二分查找)
Update:原来的洛谷U21715已成坑qwq
已经被某位管理员巨佬放进公共题库啦!又可以多一个AC记录啦!
其实也可以到这里交啦
思路分析
动态维护树的重心
题目中说到国家的首都会选在某个使得其他城市到它距离之和最小的城市,那不就是树的重心了嘛。树的重心性质真的很好,看看wuhulala巨佬的这篇博客。
网上大多数解法都是启发式合并。利用了以重心为根的子树大小不超过原树的一半,每次合并两颗树的时候,小的往大的上面合并,而且是一个点一个点地link上去,每次link完检查一下这个子树如果超过了当前整个树大小的一半,就把重心向当前点移动一下。这样做,合并次数上限\(O(N\log N)\),每次更新重心也需要\(O(\log N)\)的复杂度,所以复杂度极限是\(O(N\log^2N)\),还不够优秀。
因此我就想再优化一下算法。复杂度瓶颈就在于一个一个link,能不能考虑直接连接两颗树对重心的影响呢?
答案是肯定的。又要用到一个性质——连接两颗树后,新的重心一定在原来的两个重心的路径上。这个证明不难,反证一下就好了:对于在路径上的点来说,它的最大子树一定是往原来两个重心方向上的那两个子树的其中一个,它的其它子树肯定比这个最大子树要小,不然重心就不会是那两个点了;而对于不在路径上的点,它的最大子树是往原来两个重心方向上的那个子树,这显然劣于在路径上的点。
那就可以直接link然后把链提出来去找了。我们当然不能把整条链全找遍了,毕竟重心的性质那么多,也要好好利用嘛。
我们可以这样形象地理解:提出来的链就是一个序列,每个点都带了一些若干大小的虚子树。把子树大小和看成序列的权值,而最接近中位数的地方就是重心了。
(下面s和si的定义和蒟蒻的LCT总结中是一样的)
具体找法:类似树上二分,我们需要不断逼近树的重心的位置。记下lsum表示当前链中搜索区间左端点以左的子树大小,rsum表示右端点以右的。x的整个子树就表示了当前搜索区间,在中序遍历中x把搜索区间分成了左右两块(在Splay中对应x的左子树和右子树)。
如果x左子树的s加上lsum和x右子树的s加上rsum(对应原树中x往原来两个重心方向上的两个子树大小)都不超过新树总大小的一半,那么x当然就是重心啦!之前已经提到x其它的子树大小就不用考虑了。当然,如果总大小是奇数,重心只会有一个,那就找到了。否则,因为必须编号最小,所以还要继续找下去。
当我们没有确定答案时,还要继续找下去,那么就要跳儿子了。x把整个链分成了左右两个部分,而重心显然会在大小更大的一部分中,这个也应该好证明。如果左边比右边小,那就跳右儿子继续找。这时候当前搜索区间减小了,搜索区间以外的部分增大了,lsum应该加上si[x]+1。反之亦然。如果跳进了空儿子,那肯定所有情况都考虑完了,直接结束查找。
当然,重心找到了就还是要伸展一下,保证复杂度。(蒟蒻造数据的时候懒得去卡了qwq)
这一部分套用Splay的复杂度,是均摊\(O(\log N)\)的,总复杂度也就降到了\(O(N\log N)\)。
findroot实在很慢,于是可以写个并查集来维护每个点所在树的重心。
#include<cstdio>
#include<cstdlib>
#define R register int
#define I inline void
const int N=100009,INF=2147483647;
int f[N],c[N][2],si[N],s[N],h[N];
bool r[N];
#define lc c[x][0]
#define rc c[x][1]
inline bool nroot(R x){return c[f[x]][0]==x||c[f[x]][1]==x;}
I pushup(R x){
s[x]=s[lc]+s[rc]+si[x]+1;
}
I pushdown(R x){
if(r[x]){
R t=lc;lc=rc;rc=t;
r[lc]^=1;r[rc]^=1;r[x]=0;
}
}
I pushall(R x){
if(nroot(x))pushall(f[x]);
pushdown(x);
}
I rotate(R x){
R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
if(nroot(y))c[z][c[z][1]==y]=x;
f[f[f[c[c[x][!k]=y][k]=w]=y]=x]=z;pushup(y);//为三行rotate打call
}
I splay(R x){
pushall(x);
R y;
while(nroot(x)){
if(nroot(y=f[x]))rotate((c[f[y]][0]==y)^(c[y][0]==x)?x:y);
rotate(x);
}
pushup(x);
}
I access(R x){
for(R y=0;x;x=f[y=x]){
splay(x);
si[x]+=s[rc];
si[x]-=s[rc=y];
pushup(x);
}
}
I makeroot(R x){
access(x);splay(x);
r[x]^=1;
}
I split(R x,R y){
makeroot(x);
access(y);splay(y);
}
I link(R x,R y){
split(x,y);
si[f[x]=y]+=s[x];
pushup(y);
}
int geth(R x){
if(h[x]==x)return x;
return h[x]=geth(h[x]);
}
inline int update(R x){
R l,r,ji=s[x]&1,sum=s[x]>>1,lsum=0,rsum=0,newp=INF,nowl,nowr;
while(x){
pushdown(x);//注意pushdown
nowl=s[l=lc]+lsum;nowr=s[r=rc]+rsum;
if(nowl<=sum&&nowr<=sum){
if(ji){newp=x;break;}//剪枝,确定已经直接找到
else if(newp>x)newp=x;//选编号最小的
}
if(nowl<nowr)lsum+=s[l]+si[x]+1,x=r;
else rsum+=s[r]+si[x]+1,x=l;//缩小搜索区间
}
splay(newp);//保证复杂度
return newp;
}
#define G ch=getchar()
#define gc G;while(ch<'-')G
#define in(z) gc;z=ch&15;G;while(ch>'-')z*=10,z+=ch&15,G;
int main(){
register char ch;
R n,m,x,y,z,Xor=0;
in(n);in(m);
for(R i=1;i<=n;++i)s[i]=1,h[i]=i,Xor^=i;
while(m--){
gc;
switch(ch){
case 'A':in(x);in(y);link(x,y);
split(x=geth(x),y=geth(y));//提出原重心路径
z=update(y);
Xor=Xor^x^y^z;
h[x]=h[y]=h[z]=z;//并查集维护好
break;
case 'Q':in(x);printf("%d\n",geth(x));break;
case 'X':gc;gc;printf("%d\n",Xor);
}
}
return 0;
}
洛谷P4299 首都(BZOJ3510)(LCT,树的重心,二分查找)的更多相关文章
- 洛谷4299首都(LCT维护动态重心+子树信息)
这个题目很有意思 QWQ 根据题目描述,我们可以知道,首都就是所谓的树的重心,那么我们假设每颗树的重心都是\(root\)的话,对于每次询问,我们只需要\(findroot(x)\)就可以. 那么如何 ...
- [洛谷P4299] 首都
题目传送门 还是维护子树信息. 但是这里多了一个找重心的操作. 这里有一个关于树重心的结论,据说可以用反证法证明.反正我不会证 就是:新的重心一定在原来两个重心之间的那条树链上. 这样我们逐步缩小搜索 ...
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- 洛谷p3384【模板】树链剖分题解
洛谷p3384 [模板]树链剖分错误记录 首先感谢\(lfd\)在课上调了出来\(Orz\) \(1\).以后少写全局变量 \(2\).线段树递归的时候最好把左右区间一起传 \(3\).写\(dfs\ ...
- 洛谷P4332 [SHOI2014]三叉神经树(LCT,树剖,二分查找,拓扑排序)
洛谷题目传送门 你谷无题解于是来补一发 随便百度题解,发现了不少诸如树剖\(log^3\)LCT\(log^2\)的可怕描述...... 于是来想想怎么利用题目的性质,把复杂度降下来. 首先,每个点的 ...
- 洛谷P3459 [POI2007]MEG-Megalopolis(树链剖分,Splay)
洛谷题目传送门 正解是树状数组维护dfn序上的前缀和,这样的思路真是又玄学又令我惊叹( 我太弱啦,根本想不到)Orz各路Dalao 今天考了这道题,数据范围还比洛谷的小,只有\(10^5\)(害我复制 ...
- 洛谷 P3384 【模板】树链剖分
树链剖分 将一棵树的每个节点到它所有子节点中子树和(所包含的点的个数)最大的那个子节点的这条边标记为"重边". 将其他的边标记为"轻边". 若果一个非根节点的子 ...
- [洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?
其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数 ...
- 点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)
推荐YCB的总结 推荐你谷ysn等巨佬的详细题解 大致流程-- dfs求出当前树的重心 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成) 容斥减去不合法情况(两条子路径 ...
随机推荐
- pv,uv的意义
PV(page view),即页面浏览量,或点击量;通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标. 高手对pv的解释是,一个访问者在24小时(0点到24点)内到底看了你网站几个页面.这里 ...
- Maven的继承与聚合——多模块开发
一:Maven多模块项目,适用于一些比较大的项目,通过合理的模块拆分,实现代码的复用,便于维护和管理.尤其是一些开源框架,也是采用多模块的方式,提供插件集成,用户可以根据需要配置指定的模块. 二:继承 ...
- 20155311高梓云《网络对抗》逆向及Bof基础
20155311高梓云<网络对抗>逆向及Bof基础 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任 ...
- jsp页面中日期的格式化
在一次开发中,由于数据库中生日采用的是datetime的数据类型,因此数据库中数据格式为:2017-07-11 00:00:00. 但是,编辑页面中回显生日肯定是不可以显示出时分秒的, ...
- 利用git将项目上传到github
本文主要介绍如果用git将项目上传到githup. 一.准备工作 (1)欲将项目上传到githup,先在githup上新建一个仓库.这里就不介绍. (2 ...
- [UOJ#268]. 【清华集训2016】数据交互[动态dp+可删堆维护最长链]
题意 给出 \(n\) 个点的树,每个时刻可能出现一条路径 \(A_i\) 或者之前出现的某条路径 \(A_i\) 消失,每条路径有一个权值,求出在每个时刻过后能够找到的权值最大的路径(指所有和该路径 ...
- 通过实例来理解paxos算法
背景 Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的”La”,此人现在在微软研究院)于1990年提出的一种基于消息传递的一致性算法.由于算法难以理解起初并没有 ...
- idea使用actiBPM插件中文乱码
idea 安转activiti插件后,编辑流程图发现保存后中文乱码,并且idea的字符集(Settings—>Editor—>File Encodings)已经设置为UTF-8,流程图中中 ...
- Invalid AABB inAABB UnityEngine.Canvas:SendWillRenderCanvases()的解决办法
我遇到这个问题的情况是, 在Start()中直接使用WWW价值本地图片,可能是加载图片相对比较耗时,就出现了这个错误. 解决的办法是使用协程: // Use this for initializati ...
- java第二次实验报告
课程:Java实验 班级:201352 姓名:池彬宁 学号:20135212 成绩: 指导教师:娄佳鹏 实验日期:15.05.05 实验密级: ...