题目描述
企鹅国的城市结构是一棵树,有 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;
}
本题结。