题目链接
题目思路
显然是在树上找最短路的问题,那么就要想到lca
因为所有由 s->t的路径我们都能看成 s ->lca(s,t)->t。很明显 s->lca(s,t) 和 lca(s,t)->t 都是一条链。
关键 :L( s ,t )=depth( s )+depth( t )-2*depth ( lca ( s , t ) )
最后要注意分类讨论即可,由于每两个点的距离都是1所以直接用深度代替长度即可,完全没有必要再去开一个数组
易错警示
无向边一定要开两倍大小(找了2小时bug,最主要是学校oj神助攻,居然会把re报错成tle)
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=3e5+5;
int n,m,k,head[maxn],cnt,u,v,lg[maxn],fa[maxn][30],depth[maxn],last[maxn];
struct node
{
int to,next;
}e[maxn<<1];
void add(int u,int v)//链式前向星建图
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int son,int father)//预处理
{
fa[son][0]=father;
depth[son]=depth[father]+1;
for(int i=1;i<=lg[depth[son]];i++)
{
fa[son][i]=fa[fa[son][i-1]][i-1];
}
for(int i=head[son];i;i=e[i].next)
{
if(e[i].to!=father)
{
dfs(e[i].to,son);
}
}
}
int lca(int x,int y)//lca板子
{
if(depth[x]<depth[y])
swap(x,y);
while(depth[x]>depth[y])
{
x=fa[x][lg[depth[x]-depth[y]]-1];
}
if(x==y)
return x;
for(int k=lg[depth[x]]-1;k>=0;k--)
{
if(fa[x][k]!=fa[y][k])
{
x=fa[x][k],y=fa[y][k];
}
}
return fa[x][0];
}
int main()
{
for(int i=1;i<=3e5;i++)//log2(i)+1
{
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
int dad=lca(u,v);
if(depth[u]+depth[v]-2*depth[dad]<=k)//在终点
{
last[v]++;
}
else if(depth[u]-depth[dad]>=k)//在左边
{
int len=depth[u]-k;//终点深度
while(depth[u]>len)
{
u=fa[u][lg[depth[u]-len]-1];
}
last[u]++;
}
else//在右边
{
int len=depth[v]-(depth[v]-depth[dad]-(k-(depth[u]-depth[dad])));//右链需要往上爬的长度为右链长-(k-左链的长度)
//len代表终点深度
while(depth[v]>len)
{
v=fa[v][lg[depth[v]-len]-1];
}
last[v]++;
}
}
for(int i=1;i<n;i++)
{
printf("%d ",last[i]);
}
printf("%d",last[n]);
return 0;
}