题意:给出一个无根树,统计有多少节点对之前的距离恰好等于K。相邻节点间的距离为1.
思路:树形DP。
我们可以将无根树转化成有根树,这个很容易做到。
对于一个子树,其根结点为s,我们只需统计s的子树之间距离为K节点对即可。然后对每个子树都进行相同的统计,就可以得到最终的答案。
因为这些节点对是经过根结点s,所以他们和根结点的距离之和就是K。所以我们要知道在以s为根的子树下,有多少个距离为i的节点。然后在利用计数的乘法原理就可以了。
但是还要考虑的一个问题是如何去重。只是简单的统计出节点s的所有儿子的距离分布,会让我们丧失太多的信息,因为我们不知道这些儿子位于哪棵子树中。因为求节点s的所有儿子的距离的时候,是按照一个一个子树来求的,这个过程已经非常自然的帮我们把子树划分开。所以,我们要在这个过程中先求出当前子树和前面所有子树产生了多少满足条件的节点对,再把当前子树的节点加入到根结点上。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 50010;
int N,K;
int to[MAX*2],nxt[MAX*2],head[MAX],tot;
long long s[50010][510];
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void addedge(int u, int v)
{
to[tot] = v, nxt[tot] = head[u];
head[u] = tot++;
to[tot] = u, nxt[tot] = head[v];
head[v] = tot++;
}
long long dfs(int u, int p)
{
s[u][0] = 1;
long long ans = 0;
for(int i = head[u]; ~i ; i = nxt[i]){
int v = to[i];
if(p == v) continue;
ans += dfs(v,u);
for(int i = 0; i < K; ++i)
ans += s[u][i] * s[v][K - i - 1];
for(int i = 1; i < K; ++i)
s[u][i] += s[v][i-1];
}
return ans;
}
int main(void)
{
//freopen("input.txt","r",stdin);
init();
scanf("%d%d",&N,&K);
for(int i = 0; i < N - 1; ++i){
int u, v;
scanf("%d%d",&u,&v);
addedge(u,v);
}
printf("%I64d\n",dfs(1,0));
return 0;
}