[NOIP2017模拟]一样远

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

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

输入格式
第一行一个整数 N,代表城市个数。
接下来 N-1 行,每行两个数字 F 和 T ,表示城市 F 和城市 T 之间有一条道路。
接下来一行一个整数 M 代表询问次数。
接下来 M 行,每行两个数字 A 和 B ,表示这次询问的城市 A 和城市 B(A可能与B相同)。

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

样例数据1
输入

4
1 2
2 3
2 4
2
1 2
1 3

输出

0
2

样例数据2
输入

4
1 2
2 3
2 4
2
1 1
3 3

输出

4
4

备注
【数据范围】
对于 30% 的数据:N,M≤1000;
对于另外 10% 的数据:A=B;
对于另外 30% 的数据:保证树的形态随机;
对于 100% 的数据:1≤N,M≤100000。

分析:这题看着十分简单啊,我在AC第一题后看到这道题真是十分开心,所以写炸了30分(而且是保证树的形态随机那30%……)。显然这是求两个点的LCA,然后判断:
1、如果距离为奇数,那就没有距离一样的点,输出0
2、如果两个点就是同一个点,那么整棵树都是满足的输出n
3、如果距离为偶数,且x的深度不等于y的深度,那么中点由深度较深的点倍增找到,满足的点便是中点及其子树(不包含有x、y其中一个的那棵子树),如图(红色圈出部分):
这里写图片描述
4、如果距离为偶数,且x的深度等于y的深度,那么中点就是LCA,满足的点便是整棵树减去LCA包含x、y的那两颗子树,如图(红色圈出部分):
这里写图片描述
我就是这一步错了,我使用LCA的所有子树去减的,应该是打到这里和情况3搞混了orz(还得感谢不随机的数据没有考到dep相同的情况hhhh)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;isdigit(ch);ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int maxn=100010;
int n,m,ans;
int tot,first[maxn],nxt[maxn*2],to[maxn*2];
int dep[maxn],size[maxn],fa[maxn][20];
bool visit[maxn];

void addedge(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
    tot++;
    nxt[tot]=first[y];
    first[y]=tot;
    to[tot]=x;
}

void buildtree(int u)
{
    for(int p=first[u];p;p=nxt[p])
    {
        int v=to[p];
        if(!visit[v])
        {
            visit[v]=true;
            dep[v]=dep[u]+1;
            fa[v][0]=u;
            for(int i=1;i<=17;++i)
                fa[v][i]=fa[fa[v][i-1]][i-1];
            buildtree(v);
            size[u]+=size[v];
        }
    }
}

int lca(int x,int y)
{
    int len=dep[x]-dep[y];
    for(int i=17;i>=0;--i)
        if(len&(1<<i))
            x=fa[x][i];

    if(x==y)
        return x;
    for(int i=17;i>=0;--i)
        if(fa[x][i]!=fa[y][i])
        {
            x=fa[x][i];
            y=fa[y][i];
        }

    return fa[x][0];
}

int main()
{
    freopen("equal.in","r",stdin);
    freopen("equal.out","w",stdout);

    int x,y;
    n=getint();
    for(int i=1;i<n;++i)
    {
        x=getint(),y=getint();
        addedge(x,y);
    }

    for(int i=1;i<=n;++i)
        size[i]=1;

    visit[1]=true;
    dep[1]=0;
    buildtree(1);//记录每的个点子节点个数即每个点的深度

    m=getint();
    while(m--)
    {
        x=getint(),y=getint();
        if(x==y)//出现在同一处,随便哪个点都满足,所以就是整棵树的大小
        {
            cout<<size[1]<<'\n';
            continue;
        }

        if(dep[x]<dep[y])
            swap(x,y);
        int z=lca(x,y);
        int len=dep[x]+dep[y]-2*dep[z];//LCA求距离
        if(len%2)//奇数,输出0
        {
            cout<<0<<'\n';
            continue;
        }

        if(dep[x]==dep[y])//在x、y的dep相同
        {
            len=len/2-1;//因为求的是中点的两棵子树,也就是到z的儿子,所以两个点向上跳的长度是一半减一
            int xx=x,yy=y;
            for(int i=17;i>=0;--i)
                if(len&(1<<i))
                {
                    xx=fa[xx][i];
                    yy=fa[yy][i];
                }

            ans=size[1]-size[xx]-size[yy];//就是这里写错了!曾经写的是size[fa[xx][0]]-size[xx]-size[yy]
        }
        else//两个dep不同
        {
            len=len/2-1;
            int xx=x;
            for(int i=17;i>=0;--i)
                if(len&(1<<i))
                    xx=fa[xx][i];

            ans=size[fa[xx][0]]-size[xx];
        }
        cout<<ans<<'\n';
    }

    return 0;
}

本题结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值