\(Description\)
一棵树是一个不含环的连通图。
树上两点间的距离是两点间最短路径的长度(边的)。
给你一棵\(n\)个点的树和一个正整数\(k\)。找出不同的距离为\(k\)的点对的数量。注意点对\((u,v)\)和\((v,u)\)被认为是相同的。
\(Input\)
第一行包含两个整数\(n\)和\(k\) \((1≤n≤50000,1≤k≤500)\)——点数和要求的两点间距离。
接下来\(n-1\)行代表了边\(“a_{i}\) \(b_{i}”\)(没有引号)\((1≤a_{i},b_{i}≤n,a_{i}≠b_{i})\),\(a_{i}\)和\(b_{i}\)是第\(i\)条边的端点。所有给出的边都是不同的。
\(Output\)
输出单独一个整数——不同的距离正好为\(k\)的树上点对的数量。
\(Sample Input\)
样例输入1
5 2
1 2
2 3
3 4
2 5
样例输入2
5 3
1 2
2 3
3 4
4 5
\(Sample Output\)
样例输出1
4
样例输出2
2
\(HINT\)
第一个样例的距离为\(2\)的点对是\((1,3)\),\((1,5)\),\((3,5)\)和\((2,4)\)。
\(Source\)
练习题 树1-树形DP
思路
我们考虑计算出对于以\(u\)为根的子树中,距离\(u\)各种距离的节点的个数,这个值可以通过\(u\)的儿子\(v\)来更新
所以我们选择树形\(dp\)
我们设一个数组\(sum[u][t]\),表示以\(u\)为根的子树中,距离\(u\)的距离为\(t\)的节点的个数
那么答案\(ans\)就是\(\sum_{j=0}^{k-1}sum[u][j]\times sum[v][k-j-1]\)
这个简单解释下:枚举与\(u\)距离\(j\)的点,因为要统计距离为\(k\)的点个数,于是我们还需要距离\(k-j\)的点,因为通过\(v\)转移 ,有一个距离\(1\),所以只需要找与\(v\)距离\(k-j-1\)的点的个数,乘起来就可以了
接下来,来讲讲怎么转移方程
也十分简单:\(\sum_{j=1}^{k}sum[u][j]+=sum[v][j-1]\)
很好理解那就不细讲了
于是我们就可以一遍\(dfs\)处理出\(sum\)数组和答案,最后直接输出就好了
代码
#include<bits/stdc++.h>
using namespace std;
const int N=50010;
int n,k,cnt=0;
int to[N<<1],nxt[N<<1],head[N];
int sum[N][510];
int ans=0;
inline void add(int u,int v)
{
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u,int fa)
{
sum[u][0]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa)continue;
dfs(v,u);
for(int j=0;j<k;j++)ans+=sum[u][j]*sum[v][k-j-1];
for(int j=1;j<=k;j++)sum[u][j]+=sum[v][j-1];
}
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
int main()
{
n=read(),k=read();
int a,b;
for(int i=1;i<n;i++)
{
a=read(),b=read();
add(a,b);add(b,a);
}
dfs(1,0);
printf("%d",ans);
return 0;
}
原文地址:https://www.cnblogs.com/ShuraEye/p/11617056.html