传送门

一道挺妙的题。


对于询问点(u,v),如右图所示,我们可以发现存在一个点m在u->v的路径中,m子树的点到u是最近的,m子树外到v是最近的。其中dis(u,m)=(dis(u,v)-1)/2,且deep[u]>deep[v]



根据这个结论,问题转换为m子树中找出距离u最大的点,在m子树外找出距离v的最大的点。

子树的信息维护最大值自然可以想到dfs序+线段树。

维护的算法步骤:

  1. 求出每个点到根节点的距离dis[i]
  2. 对所有的询问离线成2个数组ans1,ans2,ans1记录询问点对中深度大的点+mid值,ans2记录询问点对中深度小的点+mid值
  3. 对dfs树进行一次dfs,向下走一步,对于每个点,子树内的点距离当前子树根的距离-1,子树外的点距离当前子树根的距离+1
  4. 统计每个点作为询问点的答案
  5. 对于每组询问取ans1[i],ans2[i]较大的。

然后码一码就能过了。

其实昨天写的那个在线做法也能做,但细节比较多。

代码:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=1e6+5,inf=-0x3f3f3f3f;
int n,m,first[N],in[N],out[N],dep[N],tot=0,cnt=0,pred[N],st[N][21],ans[N][2],p1=1,p2=1;
struct edge{int v,next;}e[N<<1];
struct Node{int l,r,mx,lz;}T[N<<2];
struct Qu{
	int rt,pos,id;
	friend inline bool operator<(const Qu&a,const Qu&b){return in[a.rt]==in[b.rt]?in[a.pos]<in[b.pos]:in[a.rt]<in[b.rt];}
}q1[N],q2[N];
inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
inline void dfs1(int p){
	pred[in[p]=++tot]=p;
	for(int i=1;i<=20;++i)st[p][i]=st[st[p][i-1]][i-1];
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==st[p][0])continue;
		dep[v]=dep[p]+1,st[v][0]=p,dfs1(v);
	}
	out[p]=tot;
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int tmp=dep[x]-dep[y];
	for(int i=20;~i;--i)if((tmp>>i)&1)x=st[x][i];
	if(x==y)return x;
	for(int i=20;~i;--i)if(st[x][i]^st[y][i])x=st[x][i],y=st[y][i];
	return st[x][0];
}
inline int find(int p,int len){
	for(int i=20;~i;--i)if((len>>i)&1)p=st[p][i];
	return p;
}
inline void pushup(int p){T[p].mx=max(T[lc].mx,T[rc].mx);}
inline void pushnow(int p,int v){T[p].lz+=v,T[p].mx+=v;}
inline void pushdown(int p){if(T[p].lz)pushnow(lc,T[p].lz),pushnow(rc,T[p].lz),T[p].lz=0;}
inline void build(int p,int l,int r){
	T[p].l=l,T[p].r=r;
	if(T[p].l==T[p].r){T[p].mx=dep[pred[l]];return;}
	build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int ql,int qr,int v){
	if(ql>T[p].r||qr<T[p].l)return;
	if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,v);
	pushdown(p);
	if(qr<=mid)update(lc,ql,qr,v);
	else if(ql>mid)update(rc,ql,qr,v);
	else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
	pushup(p);
}
inline int query(int p,int ql,int qr){
	if(ql>T[p].r||qr<T[p].l)return inf;
	if(ql<=T[p].l&&T[p].r<=qr)return T[p].mx;
	pushdown(p);
	if(qr<=mid)return query(lc,ql,qr);
	if(ql>mid)return query(rc,ql,qr);
	return max(query(lc,ql,mid),query(rc,mid+1,qr));
}
inline void dfs2(int p){
	while(p1<=m&&q1[p1].rt==p)ans[q1[p1].id][0]=query(1,in[q1[p1].pos],out[q1[p1].pos]),++p1;
	while(p2<=m&&q2[p2].rt==p)ans[q2[p2].id][1]=max(query(1,1,in[q2[p2].pos]-1),query(1,out[q2[p2].pos]+1,n)),++p2;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==st[p][0])continue;
		update(1,in[v],out[v],-1),update(1,1,in[v]-1,1),update(1,out[v]+1,n,1);
		dfs2(v);
		update(1,in[v],out[v],1),update(1,1,in[v]-1,-1),update(1,out[v]+1,n,-1);
	}
}
int main(){
	freopen("lx.in","r",stdin);
	n=read();
	for(int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u);
	dfs1(1),build(1,1,n),m=read();
	for(int i=1,d,x,y,t,md;i<=m;++i){
		x=read(),y=read(),t=lca(x,y),d=dep[x]+dep[y]-2*dep[t];
		if(dep[x]<dep[y])swap(x,y);
		md=find(x,(d-1)/2),
		q1[i]=(Qu){x,md,i},q2[i]=(Qu){y,md,i};
	}
	sort(q1+1,q1+m+1),sort(q2+1,q2+m+1),dfs2(1);
	for(int i=1;i<=m;++i)printf("%d\n",max(ans[i][0],ans[i][1]));
	return 0;
}

2018.11.01 NOIP训练 图论(线段树+倍增+dfs序)的更多相关文章

  1. 2018.11.01 NOIP训练 树的排列(树形dp)

    传送门 跟这道题差不多. 只不过是让权值小的儿子做权值大的儿子的父亲而已. 代码

  2. 2018.11.01 NOIP训练 梭哈(模拟)

    传送门 这题貌似不考智商啊. 直接按题意写就可以了. 事实上把牌从小到大排序之后写起来很舒服的. 然后就是有些地方可以人脑减代码量和判断次数. (提示:满堂红和某几种同类型的牌的大小判断) 然后注意A ...

  3. 2018.11.01 NOIP训练 木棒分组(搜索+剪枝)

    传送门 测试搜索的时候状态定义错了233. 我们把木棒从大到小排序. 然后保证每一组搜到的木棒出现的长度是从大到小递减的. 直接定义现在搜的木棒从什么位置开始,当前这一组的总长度,之前几组的总长度. ...

  4. 2018.11.01 NOIP训练 递增数列(迭代加深)

    传送门 直接迭代加深搜索. 发现每次最多增加一倍,最少增加一,于是果断上下界剪枝. 代码

  5. 2018.11.01 NOIP训练 某种密码(折半搜索)

    传送门 直接折半搜索,把所有和装到unorderedmapunordered_mapunorderedm​ap里面最后统计答案就行了. 然后考试的时候读优并没有处理有负数的情况于是爆零了 代码

  6. 2018.11.01 NOIP训练 cost数(搜索+容斥原理)

    传送门 唉考试的时候忘记剪倍数的枝了666666分滚粗. 其实就是一直取lcmlcmlcm搜索,然后容斥原理统计就行了. 代码

  7. CF877E Danil and a Part-time Job 线段树维护dfs序

    \(\color{#0066ff}{题目描述}\) 有一棵 n 个点的树,根结点为 1 号点,每个点的权值都是 1 或 0 共有 m 次操作,操作分为两种 get 询问一个点 x 的子树里有多少个 1 ...

  8. 【HIHOCODER 1576】 子树中的最小权值(线段树维护DFS序)

    描述 给定一棵N个节点的树,编号1~N.其中1号节点是根,并且第i个节点的权值是Vi. 针对这棵树,小Hi会询问小Ho一系列问题.每次小Hi会指定一个节点x,询问小Ho以x为根的子树中,最小的权值是多 ...

  9. CodeForces 343D 线段树维护dfs序

    给定一棵树,初始时树为空 操作1,往某个结点注水,那么该结点的子树都注满了水 操作2,将某个结点的水放空,那么该结点的父亲的水也就放空了 操作3,询问某个点是否有水 我们将树进行dfs, 生成in[u ...

随机推荐

  1. JS获取鼠标左(右)滑事件

    鼠标左(右)滑也是网站开发中常见的效果之一,这里对鼠标左(右)滑做出一些解释. 首先要获取需要左右滑事件的节点: eg: var div=document.getElementById("d ...

  2. Unity5权威讲解+项目源码+MP4

    扫码时备注或说明中留下邮箱 付款后如未回复请至https://shop135452397.taobao.com/ 联系店主

  3. SSRF攻击-运用gopher协议构造POST包--emmmm(http://10.112.68.215:10004/index.php?action=login)

        还是天枢的一道CTF题,启程!       分析题目,自己注册账户并登陆后,提示输入一个url,网站会自己运行查找网页信息.     emmmmm,很挑衅,网站就当作服务器,我们在url框中输 ...

  4. crm作业知识点集合[二]

    知识点1 前面我们实现了这个功能,就是在models中如果有了choice选项,我们可以实现在页面显示这个chocice的value值,而不是key值,我们这个知识点就是在优化一下这个点 首先如果表中 ...

  5. Java项目学习笔记(一)

    2017/2/27 一.target属性 <a>标签的target属性规定在什么地方打开该链接文档. 1.打开新窗口,将文档重定向到一个单独的窗口. <a href="a. ...

  6. Python爬虫项目--爬取猫眼电影Top100榜

    本次抓取猫眼电影Top100榜所用到的知识点: 1. python requests库 2. 正则表达式 3. csv模块 4. 多进程 正文 目标站点分析 通过对目标站点的分析, 来确定网页结构,  ...

  7. mysql中left join设置条件在on与where时的区别

    一.首先我们准备两张表来进行测试. CREATE TABLE `a` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `na ...

  8. IE6、7下overflow:hidden失效的问题

    问题产生原因: 当父元素的直接子元素或者下级子元素的样式拥有position:relative或者position:absolute属性时,父元素的overflow:hidden属性就会失效. 例如: ...

  9. session是什么

    初识session,跟大家一起学习下 session是什么 首先,我们需要知道session是什么.我们普遍将session称之为会话控制.说实在的,我现在也不清楚session到底算是什么.我个人认 ...

  10. 虚拟机vmware centos7 扩展磁盘空间

    0.思路 创建一个新的逻辑分区,将新的逻辑分区格式化ext3(或其他类型)的文件系统,mount到磁盘空间不够的文件系统,就跟原来的分区/文件系统一样的使用 1.准备 1.1 注意使用VMware自带 ...