Snow的追寻--线段树维护树的直径
Snow终于得知母亲是谁,他现在要出发寻找母亲。王国中的路由于某种特殊原因,成为了一棵有n个节点的根节点
为1的树,但由于"Birds are everywhere.",他得到了种种不一样的消息,每份消息中都会告诉他有两棵子树是禁
忌之地,于是他向你求助了。他给出了q个形如"x y"的询问,表示他不能走到x和y的子树中,由于走的路径越长他
遇见母亲的概率越大但是他只能走一条不经过重复节点的路径,现在他想知道对于每组询问他能走的最长路径是多
少,如果没有,输出零。
第一行两个正整数n和q(1≤n,q≤100000)
第二到第n行每行两个整数u,v表示u和v之间有一条边连接,边的长度为1。
接下来q行每行两个x,y表示一组询问,意义如题目描述。
1≤n≤100000,1<=q<=50000
Output
q行,输出见题目描述
Sample Input
5 2
1 3
3 2
3 4
2 5
2 4
5 4
Sample Output
1
2
样例解释
询问1中2和4的子树不能走,最长路径为(1,3)长度为1
询问2中5和4的子树不能走,最长路径为(1,3,2)长度为2
Sol:
很明显的每个询问就是在求将两棵子树去掉后剩下的树的直径。我们先可以得出该树的dfs序,那么对于一颗子树就变成了序列上的一个区间,那么我们可以用线段树,维护一个区间表示的点的直径,对于两个区间,直径的合并就是从四个端点中任选两个连成的路径,选出其中长度最长的,即为合并后的直径,时间复杂度O(N*log2N)
/*
对树进行dfs遍历,形成一个长度为N的序列。
要去掉的两个子树,在dfs序中是连续的。
从整个序列中去掉这两个序列,可能形成二个或三个连续的序列
对序列进行合并求直径。
每个序列有左右端点,形成的新的直径,有四种选择。对于端点之间的距离利用lca来求就好了。
对于文后图标样例,形成一个dfs序列,其中45及78是要去掉的
12 45 6 78 3
于是合并12 6 3这三个区间就好了
*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ls now<<1,l,mid
#define rs now<<1|1,mid+1,r
#define rep(i,x) for(int i=head[x],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
using namespace std;
const int maxn=100010;
struct fk
{
int to,nxt;
}
e[maxn<<1];
int cnt,n,q,tot,head[maxn],dfn[maxn],p[maxn],last[maxn],dep[maxn],f[maxn][20];
struct fq{int sum,x,y;}
t[maxn<<2],ans;
void ins(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs(int x,int fa)
{
dfn[x]=++tot;//x进入的时间点
p[tot]=x;//第tot个点是x
f[x][0]=fa;
dep[x]=dep[fa]+1;
rep(i,x)
if(v!=fa)
dfs(v,x);
last[x]=tot;
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=19;i>=0;i--)
x=dep[f[x][i]]>dep[y]?f[x][i]:x;
if(dep[x]>dep[y])
x=f[x][0];
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return x==y?x:f[x][0];
}
int dis(int x,int y) //求x,y两点的距离
{
if(!x||!y)
return 0;
int z=lca(x,y);
return dep[x]+dep[y]-2*dep[z];}
void merge(fq &now,fq x,fq y)
//将x,y所代表的区间进行合并,结果放到now中
{
int a,b,c,d,e;
a=dis(x.x,y.x);//新直径可能为x左点与y左点的距离
b=dis(x.x,y.y);//新直径可能为x左点与y右点的距离
c=dis(x.y,y.x);
d=dis(x.y,y.y);
e=max(a,max(b,max(c,d)));//取最大值
if(a==e)
now.x=x.x,now.y=y.x,now.sum=a;
if(b==e)
now.x=x.x,now.y=y.y,now.sum=b;
if(c==e)
now.x=x.y,now.y=y.x,now.sum=c;
if(d==e)
now.x=x.y,now.y=y.y,now.sum=d;
if(x.sum>now.sum)//x区间的直径大于之
now.x=x.x,now.y=x.y,now.sum=x.sum;
if(y.sum>now.sum)//y区间的直径大于之
now.x=y.x,now.y=y.y,now.sum=y.sum;
if(!now.sum)
now.x=now.y=0;
}
void build(int now,int l,int r)
{
if(l==r)
{
t[now].x=t[now].y=p[l];
return ;
}
int mid=(l+r)>>1;
build(ls);
build(rs);
merge(t[now],t[now<<1],t[now<<1|1]);
}
void get_ans(int now,int l,int r,int x,int y)
//get_ans(1,1,n,1,dfn[u]-1);
//now根结点编号,l,r左右区间
{
if(x<=l&&r<=y)
{
merge(ans,ans,t[now]);
return ;
}
int mid=(l+r)>>1;
if(x<=mid)
get_ans(ls,x,y);
if(y>mid)
get_ans(rs,x,y);
}
int main()
{
scanf("%d%d",&n,&q);
int u,v;
for(int i=1;i<n;i++)
scanf("%d%d",&u,&v),ins(u,v),ins(v,u);
dfs(1,0);
for(int j=1;j<20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
build(1,1,n);
while(q--)
{
scanf("%d%d",&u,&v);
if(v==1||u==1) //去掉的是根结点
{
puts("0");
continue;
}
ans.sum=ans.x=ans.y=0;
if(dfn[u]>dfn[v]) //让u进入的时间更小
swap(u,v);
get_ans(1,1,n,1,dfn[u]-1);//从1开始到u进入前的
get_ans(1,1,n,last[u]+1,dfn[v]-1);//从u离开后v进来之前
if(last[v]<=last[u])
//看谁离开的时间更大,从离开后的那个时间到n之一段也要加进来
get_ans(1,1,n,last[u]+1,n);
else
get_ans(1,1,n,last[v]+1,n);
printf("%d\n",ans.sum);
}
}
参考下这个文章:https://blog.csdn.net/rzO_KQP_Orz/article/details/52280811
Snow的追寻--线段树维护树的直径的更多相关文章
- (jzoj snow的追寻)线段树维护树的直径
jzoj snow的追寻 DFS序上搞 合并暴力和,记录最长链和当前最远点,距离跑LCA # include <stdio.h> # include <stdlib.h> # ...
- 倍增/线段树维护树的直径 hdu5993/2016icpc青岛L
题意: 给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径 点10W,询问10W,询问相互独立 Solution: 考虑线段树/倍增维护树的直径 考虑一个点集的区间 [l, r] 而我们知道了有 ...
- 2016shenyang-1002-HDU5893-List wants to travel-树链剖分+线段树维护不同区间段个数
肯定先无脑树链剖分,然后线段树维护一段区间不同个数,再维护一个左右端点的费用. 线段树更新,pushDown,pushUp的时候要注意考虑链接位置的费用是否相同 还有就是树链剖分操作的时候,维护上一个 ...
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
- Codeforces GYM 100114 D. Selection 线段树维护DP
D. Selection Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Descriptio ...
- [BZOJ 3995] [SDOI2015] 道路修建 【线段树维护连通性】
题目链接:BZOJ - 3995 题目分析 这道题..是我悲伤的回忆.. 线段树维护连通性,与 BZOJ-1018 类似,然而我省选之前并没有做过 1018,即使它在 ProblemSet 的第一页 ...
- [BZOJ 1018] [SHOI2008] 堵塞的交通traffic 【线段树维护联通性】
题目链接:BZOJ - 1018 题目分析 这道题就说明了刷题少,比赛就容易跪..SDOI Round1 Day2 T3 就是与这道题类似的..然而我并没有做过这道题.. 这道题是线段树维护联通性的经 ...
- HDU3564 --- Another LIS (线段树维护最值问题)
Another LIS Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- CodeForces 343D 线段树维护dfs序
给定一棵树,初始时树为空 操作1,往某个结点注水,那么该结点的子树都注满了水 操作2,将某个结点的水放空,那么该结点的父亲的水也就放空了 操作3,询问某个点是否有水 我们将树进行dfs, 生成in[u ...
随机推荐
- (转) 【oracle调优】优化全表扫---cache整张表或索引
情景分析: 1)某查询必须要走全表扫描 2)该查询执行的频率相当高 3)对执行时间的要求也相当苛刻的话 4)数据库的IO比较吃紧 5)数据库的内存比较宽松 6)该表的大小没有大到离谱 以上情况下,可以 ...
- DevExpress v19.1新版亮点——WinForms篇(五)
行业领先的.NET界面控件DevExpress v19.1终于正式发布,本站将以连载的形式介绍各版本新增内容.在本系列文章中将为大家介绍DevExpress WinForms v19.1中新增的一些控 ...
- 使用vux的x-input组件中show-clear=“true”清除icon点击失效的问题
问题场景: 在电脑浏览器点击清除icon正常 在手机浏览器,手机微信,微信开发者工具中点击清除icon失效 查看vux中的x-input组件中的源码发现,清除icon使用了v-show显示与隐藏,对应 ...
- 美国的电信巨头T-Mobile今天披露了另一起数据遭黑客泄露事件
您是T-Mobile预付费客户吗?如果是,您应该立即创建或更新您关联的帐户PIN /密码,以提供额外的保护.总部位于美国的电信巨头T-Mobile今天披露了另一起数据泄露事件,该事件最近暴露了一些使用 ...
- 《SaltStack技术入门与实践》—— Peer
Peer 本章节参考<SaltStack技术入门与实践>,感谢该书作者: 刘继伟.沈灿.赵舜东 Peer组件是SaltStack中Minion向Master发布任务的一个组件,使用Peer ...
- Linux 环境下 gzip 的加解密命令
1.加密 [root@127-0-0-1 nginx]# gzip -v access.log-20190328 access.log-20190328: 95.8% -- replaced with ...
- eclipse在线安装ermaster插件
eclipse在线安装ermaster插件: https://www.jianshu.com/p/449fbcd9141a ERMaster的安装和使用 https://www.cnblogs.com ...
- Linux入门培训教程 常见linux命令释义
快到中午吃饭了,然后忽然想起来samba里面没有添加用户.于是乎,就玩弄起了samba. Samba三下五除二就安装好了,想想window里面不断的点击下一步,还要小心提防各种隐藏再角落里的绑定软件. ...
- CCA(典型相关分析)
https://www.cnblogs.com/pinard/p/6288716.html
- 进程and线程and协程效率对比
1.进程与进程池的效率对比 多进程:p.start()过程中,只是向操作系统发送一个信号,至于什么时候执行,都是操作系统的事情,操作系统接收到信号时,帮该进程申请一块内存空间+拷贝父进程的地址空间 # ...