zoukankan      html  css  js  c++  java
  • 【XSY2597】一样远(equal)(LCA)

    (Description)

    企鹅国的城市结构是一棵树,有(N)座城市和(N-1)条无向道路,每条道路都一样长。豆豆和豆沙准备去参加(NOIP(National Olympiad in Informatics for Penguin)),但是他们住在不同的地方,豆豆住在城市(A),豆沙住在城市(B)。他们想找一个距离(A)(B)一样远的集合地点,所以他们想知道有多少个城市满足这个要求?

    由于他们会参加很多次(NOIP),所以有很多个询问。


    (Input)

    第一行一个整数(N)代表城市个数。

    接下来(N-1)行,每行两个数字(F)(T),表示城市(F)和城市(T)之间有一条道路。

    接下来一行一个整数(M)代表询问次数。

    接下来(M)行,每行两个数字(A)(B),表示这次询问的城市(A)和城市(B)(A)可能与(B)相同)。


    (Output)

    输出(M)行,每行一个整数表示到(A)(B)一样远的城市个数。


    (SampleInput1)

    4

    1 2

    2 3

    2 4

    2

    1 2

    1 3


    (SampleOutput1)

    0

    2


    (SampleInput2)

    4

    1 2

    2 3

    2 4

    2

    1 1

    3 3


    (SampleOutput2)

    4

    4


    (Hint)

    对于(30\%)的数据:(N,M≤1000)

    对于另外(10\%)的数据:(A=B)

    对于另外(30\%)的数据:保证树的形态随机;

    对于(100\%)的数据:(1≤N,M≤100000)


    思路

    我们发现这道题给出的一棵树,于是我们考虑树上路径的算法:(LCA),树链剖分

    我们在这里选择(LCA)

    先声明一下(LCA)中常用的数组含义


    (d[a])表示节点(a)的深度

    (siz[a])表示节点(a)的子树大小(包括他自己)

    (f[a][i])表示(a)(2^i)父亲


    我们考虑在什么情况下会存在与(a,b)相同距离的点。设这个点为(c)

    可以发现,当且仅当(a)(c) 的距离等于(b)(c)的距离,c存在

    并且,我们可以发现,(c) 必定在(a)(lca)的路径上或 (b)(lca) 的路径上

    综上,我们可以得到结论:当且仅当 (a)(b) 的路径长度为偶数时,(c) 存在

    (a)(b) 的路径长度 (dis=d[a]+d[b]-d[lca] imes 2,dis\%2=0)(至于为什么,自己想想)


    如果满足条件

    我们对于每一个询问的(a,b),考虑情况

    (1.) (a=b) ,显而易见答案为树的节点个数(n)

    给个总图:

    在这里插入图片描述

    (2.) (a eq b)(d[a]=d[b]) , 即在图中 (a=5,b=7)

    这样 (c) 必定为·(lca)

    可以发现,他们在 (2) 节点相遇,之后的 (1) 节点的另外两棵子树上的点以及 (14) 节点都满足条件

    于是, 得到 (ans=n-siz[5]-siz[7])

    (3.) (a eq b)(d[a]<d[b]) ,即在图中 (a=10,b=4)

    这样 (c)(3) 节点,之后的 (13) 节点满足条件

    于是,得到 (ans=siz[3]-siz[8])

    (4.) (a eq b)(d[a]>d[b])

    同上


    考虑完情况后,我们又遇到一个问题:如何在 (O(logn)) 时间内找到 (a,b) 相遇的节点,并找到应该减去子树大小的节点

    我们还是选择倍增查找

    假设 (d[a]<d[b])

    (c) 必定在 (b)(lca) 的路径上

    我们可以轻易得到 (c)(b) 的距离即 (b) 要向上跳的层数为(dis/2(dis\%2=0))

    向上跳 (dis) 层,跟 (LCA) 的实现相似,就可以实现了

    详细见代码:


    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010;
    int n;
    int to[N<<1];
    int nxt[N<<1];
    int head[N];
    int cnt=0;
    void add(int u,int v)
    {
    	to[++cnt]=v;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    int f[N][25];
    int d[N];
    int siz[N];
    void dfs(int u,int fa)
    {
    	siz[u]=1;
    	f[u][0]=fa;
    	for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v!=fa)
    		{
    			d[v]=d[u]+1;
    			dfs(v,u);
    			siz[u]+=siz[v];
    		}
    	}
    }
    int get(int x,int y)
    {
        if(d[x]<d[y])swap(x,y);
        for(int i=20;i>=0;i--)
        {
            if(d[f[x][i]]>=d[y])
            {
                x=f[x][i];
            }
        }
        if(x==y)return x;
        for(int i=20;i>=0;i--)
        {
            if(f[x][i]!=f[y][i])
            {
                x=f[x][i],y=f[y][i];
            }
        }
        return f[x][0];
    }
    //以上是LCA模板
    int id=0x7fffffff;
    int work(int &son,int a,int deep)//找到相遇的节点以及要减去子树大小的节点son,deep表示向上跳deep层
    {
    	int dep=deep-1;
    	for(int i=20;i>=0;--i)
    	{
    		if(dep>(1<<i))
    		{
    			dep-=(1<<i);
    			a=f[a][i];
    		}
    	}
    	son=a;
    	return f[a][0];
    }
    int main()
    {
    	scanf("%d",&n);
    	int a,b;
    	int tot=0;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d %d",&a,&b);
    		add(a,b);add(b,a);
    	}
    	d[1]=1;
    	int m;
    	dfs(1,0);
    	scanf("%d",&m);
    	while(m--)
    	{
    		scanf("%d %d",&a,&b);
    		if(a==b)
    		{
    			printf("%d
    ",n);
    			continue;
    		}
    		int lca=get(a,b);
    		int dis=d[a]+d[b]-d[lca]*2;
    		if((dis%2)==1)puts("0");
    		else 
    		{
    			dis/=2;
    			int sona,sonb,v;
    			if(d[a]==d[b])
    			{
    				work(sona,a,dis);
    				work(sonb,b,dis);
    				printf("%d
    ",n-siz[sona]-siz[sonb]);
    			}
    			else 
    			{
    				if(d[a]<d[b])
    				{
    					v=work(sonb,b,dis);
    				    printf("%d
    ",siz[v]-siz[sonb]);
    				}
    				else 
    				{
    					v=work(sona,a,dis);
    				    printf("%d
    ",siz[v]-siz[sona]);
    					
    				}
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    03014_properties配置文件
    Python 文件I/O
    Python面向对象
    Python CGI编程
    Python正则表达式
    Python使用SMTP发送邮件
    python操作mysql数据库
    Python多线程
    python XML解析
    给傻瓜用的SP2010开发--第一部分--理解SP开发平台--第一章节--理解SP促销讨论(2)--追踪SP源头
  • 原文地址:https://www.cnblogs.com/ShuraEye/p/11412931.html
Copyright © 2011-2022 走看看