JZOJ 数形dp练习5(树形dp入门)

来源:JZOJ

题目描述

给定一棵 n n n 个节点的树,求其中每个点到其他节点的距离和。

解题思路

  • 首先,如果是求第一个点到其他节点的距离和应该很简答吧
void dfs(int x,int father){
   sum[x]=1; //以x为根的子树中点的个数
   ans+=deep[x];   //deep是点1到点x的深度
   for (int i=linkk[x];i;i=e[i].Next)  //邻接表查询
   {
       int y=e[i].y;
       if (y!=father)
       {
   	    	deep[y]=deep[x]+e[i].v;  //深度增加
      	 	dfs(y,x);  //继续遍历
   	    	sum[x]+=sum[y];  //累加个数
       }
   }
}
  • 那么我们再来考虑每个点到其他节点的距离和JZOJ 数形dp练习5(树形dp入门)_第1张图片
  • 可以看出,对于一个点 y y y,他的父亲是 x x x,边权是 v v v
    这条边多经过的次数就是 : ( n − s u m [ y ] − 1 ) − ( s u m [ y ] − 1 ) = n − 2 ∗ s u m [ y ] (n−sum[y]−1)−(sum[y]−1)=n-2*sum[y] (nsum[y]1)(sum[y]1)=n2sum[y]
    ( n − s u m [ y ] − 1 ) (n−sum[y]−1) (nsum[y]1) 表示的是这棵树除了 y y y 的子树和 x x x 的另外节点,因为它们必须要经过这条边, ( s u m [ y ] − 1 ) (sum[y]−1) (sum[y]1) 表示的是 y y y 子树(不包括本身)的节点个数;
#include 
using namespace std;
int num[200010],deep[200010],linkk[200010];
long long dp[200010];
int n,t=0;
long long ans=0;
struct node
{
	int y,next;
	long long v;
}e[200010];
void insert(int x,int y,int v)  //邻接表查询
{
	e[++t].y=y; e[t].v=v;
	e[t].next=linkk[x]; linkk[x]=t;
}
void dfs1(int x,int father)  //求出每棵子树的节点数量
{
	num[x]=1;
	ans+=deep[x];
	for (int i=linkk[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if (y!=father)
		{
			deep[y]=deep[x]+e[i].v;
			dfs1(y,x);
			num[x]+=num[y];
		}
	}
}
void dfs2(int x,int father)
{
	for (int i=linkk[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if (y!=father)
		{
			dp[y]=dp[x]+(long long)(n-2*num[y])*e[i].v;  //公式
			dfs2(y,x);
		}
	}
}
int main()
{
	freopen("t5.in","r",stdin);
	freopen("t5.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<n;i++)
	{
		int x,y,v;
		scanf("%d %d %d",&x,&y,&v);
		insert(x,y,v);  //邻接表插入
		insert(y,x,v);
	}
	dfs1(1,0);
	dp[1]=ans;
	dfs2(1,0);
	printf("%lld",ans);
	for (int i=2;i<=n;i++) printf("\n%lld",dp[i]);
	return 0;
}

你可能感兴趣的:(树形dp)