zoukankan      html  css  js  c++  java
  • CF519E A and B and Lecture Rooms

    题意简述

    题目链接

      给定一棵无根树,要求回答m次询问,每次询问给定两个点u,v,求树上与这两个点距离相等的点的个数。距离定义为树上两点间的边数。

    算法概述

      这道题难度评定成紫色着实有点过了,个人感觉封顶蓝色。毕竟前置知识只有一个树上倍增,而且也没什么思维难度,就简单分类讨论一下就完事了。

      手动画一画样例不难发现,能够作为答案的点中,距离给定两点u,v最近的点,肯定在路径u→v上,且必然为路径的中点。对这个点分两种情况考虑:

      1. 这个点就是lca(u,v),则说明u,v深度相同。但u,v深度相同并不能直接说明这个点就是lca(u,v),因为还有一种特殊情况即u,v为同一点,特判即可,这种情况的答案显然是树上所有点都能走。然后对于这个点为lca(u,v)的情况,我们执行树上倍增的部分过程,让u,v同时上跳,跳到lca(u,v)的儿子x,y为止,那么答案即为n-size[x]-size[y],因为这个点可以走到除了这两棵子树以外的所有点,这些点都能成为答案。

      2. 这个点不是lca(u,v),那么u,v深度一定不同,不妨设u为深度大的点。假设路径中点为x,则v必然要经过lca(u,v)再向下走,才能走到x,而u只需上跳即可,设u跳到x的路上,经过的x的儿子节点为y。那么答案则需要将树上的这两部分剪除,即为size[x]-size[y]。跳的过程同样用倍增即可。

      时间复杂度O(mlogn)

    参考代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N=1e5+10,MAX_LOG=17;
    struct Edge{
    	int to,next;
    }edge[N<<1];int idx;
    int h[N];
    
    void add_edge(int u,int v){edge[++idx]={v,h[u]};h[u]=idx;}
    
    int f[N][MAX_LOG];
    int dep[N],siz[N];
    int n,m;
    
    void dfs(int p,int fa)
    {
    	dep[p]=dep[fa]+1;
    	siz[p]=1;
    	for(int i=h[p];~i;i=edge[i].next)
    	{
    		int to=edge[i].to;
    		if(to==fa)continue;
    		f[to][0]=p;
    		for(int j=1;j<MAX_LOG;j++)
    			f[to][j]=f[f[to][j-1]][j-1];
    		dfs(to,p);
    		siz[p]+=siz[to];
    	}
    }
    
    inline int lca(int x,int y)
    {
    	if(dep[x]<dep[y])swap(x,y);
    	int d=dep[x]-dep[y];
    	for(int j=MAX_LOG-1;j>=0;j--)
    		if(d>=(1<<j))d-=(1<<j),x=f[x][j];
    	if(x==y)return x;
    	for(int j=MAX_LOG-1;j>=0;j--)
    		if(f[x][j]!=f[y][j])x=f[x][j],y=f[y][j];
    	return f[x][0];
    }
    
    int main()
    {
    	scanf("%d",&n);
    	memset(h,-1,sizeof h);
    	for(int i=1;i<=n-1;i++)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		add_edge(u,v);
    		add_edge(v,u); 
    	}
    	dfs(1,0);
    	scanf("%d",&m);
    	while(m--)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		if(dep[u]==dep[v])
    		{
    			if(u==v){printf("%d
    ",n);continue;} 
    			for(int j=MAX_LOG-1;j>=0;j--)
    				if(f[u][j]!=f[v][j])u=f[u][j],v=f[v][j];
    			printf("%d
    ",n-siz[u]-siz[v]);
    		}
    		else
    		{
    			int d=dep[u]+dep[v]-2*dep[lca(u,v)];
    			if(d%2){printf("0
    ");continue;}
    			d/=2;d--;
    			if(dep[u]<dep[v])swap(u,v);
    			for(int j=MAX_LOG-1;j>=0;j--)
    				if(d>=(1<<j))d-=(1<<j),u=f[u][j];
    			int x=f[u][0];
    			printf("%d
    ",siz[x]-siz[u]);
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    我画着图,FluentAPI 她自己就生成了
    寻找性能更优秀的不可变小字典
    寻找性能更优秀的动态 Getter 和 Setter 方案
    数据治理方案技术调研 Atlas VS Datahub VS Amundsen
    数据库读写分离这个坑,你应该踩过吧?
    写了一套优雅接口之后,领导让我给大家讲讲这背后的技术原理
    年轻人不讲武德,竟然重构出这么优雅后台 API 接口
    贞炸了!上线之后,消息收不到了!
    一笔订单,但是误付了两笔钱!这种重复付款异常到底该如何解决?
    自动化运维工具之Puppet master/agent模型、站点清单和puppet多环境设定
  • 原文地址:https://www.cnblogs.com/ninedream/p/13539144.html
Copyright © 2011-2022 走看看