题解 CF161D 【Distance in Tree】
这道题可以使用点分治的方法来做。
不会点分治的可以来看看这个博客:there
我们可以这样做:将标记距离的数组(这里定义的是 alive 数组)改为 int 类型,把它重新定义为之前子树到根距离的个数,新增一个 Right 变量表示正确答案个数,我们就可以这样子做了:
for(register int j=1;j<=nowdis;++j)
if(q>=now[j])
Right+=alive[q-now[j]];
统一处理距离的时候,只要把模板题里面的 true, false 改为 ++ 和 - - 操作即可。
完整代码如下:
#include<bits/stdc++.h>
#define Maxn int(5e4)
#define Maxk int(5e4)
using namespace std;
//===============邻接表=================
struct Node
{
int to,nxt,w;
} Edge[(Maxn<<1)+5];
int tot,Head[Maxn+5];
inline void Addedge(int u,int v,int w)//加边操作
{
Edge[++tot].to=v;
Edge[tot].nxt=Head[u];
Edge[tot].w=w;
Head[u]=tot;
}
//===========找重心 & 更新距离==========
int n,q,root;
int siz[Maxn+5],maxs[Maxn+5];//siz:子树大小 maxs:最大儿子的大小
int vis[Maxn+5];//vis:代表是否删去
int sum;//sum:记录当前子树大小
void getroot(int x,int f)//找重心
{
siz[x]=1;
maxs[x]=0;
for(register int i=Head[x];i;i=Edge[i].nxt)
{
int v=Edge[i].to;
if(vis[v] || v==f) continue;
getroot(v,x);
siz[x]+=siz[v];
maxs[x]=max(maxs[x],siz[v]);
}
maxs[x]=max(maxs[x],sum-siz[x]);
root=maxs[x]<maxs[root]?x:root;
}
int dis[Maxn+5],now[Maxn+5],nowdis;
//dis:各结点到根结点的距离 now:存放这些距离 nowdis:计数器
void getdis(int x,int f)
{
now[++nowdis]=dis[x];
for(register int i=Head[x];i;i=Edge[i].nxt)
{
int v=Edge[i].to;
if(vis[v] || v==f) continue;
dis[v]=dis[x]+Edge[i].w;
getdis(v,x);
}
}
//================点分治==================
int Right;
int alive[Maxk+5];//alive:表示之前子树距离的个数
stack<int> s;//使用栈统一处理
void count(int x)//计算距离
{
for(register int i=Head[x];i;i=Edge[i].nxt)
{
int v=Edge[i].to;
if(vis[v]) continue;
nowdis=0;
dis[v]=Edge[i].w;
getdis(v,x);
for(register int j=1;j<=nowdis;++j)
if(q>=now[j])
Right+=alive[q-now[j]];
for(register int j=1;j<=nowdis;++j)//存放距离
{
s.push(now[j]);
alive[now[j]]++;
}
}
while(!s.empty())//清空
{
alive[s.top()]--;
s.pop();
}
}
void devide(int x)//点分治
{
vis[x]=true;
alive[0]=true;
count(x);
for(register int i=Head[x];i;i=Edge[i].nxt)
{
int v=Edge[i].to;
if(vis[v]) continue;
root=0;
maxs[0]=sum=siz[v];
getroot(v,0);
getroot(root,0);
devide(root);
}
}
int main()
{
scanf("%d%d",&n,&q);
for(register int i=1;i<n;++i)//连边
{
int a,b;
scanf("%d%d",&a,&b);
Addedge(a,b,1);
Addedge(b,a,1);
}
maxs[0]=sum=n;
getroot(1,0);
getroot(root,0);//找重心并更新子树大小
devide(root);
printf("%d",Right);
return 0;
}