树上DP5

题目

树上DP5

题解

  这道题就是看每一个点到其他点的距离和是多少。数据范围告诉我们 绝 对 不 能 用暴力求解(TLE自动机.jpg)。因为本题满足树的性质且很明显答案之间有某种联系(真实原因:看题目)所以考虑DP求解。
  分析题目可知,所求的值就是以第 i i i号节点为根的树上根到每一个点的距离和。
  画图后可以发现对于每一个节点,可以假设第 i i i号节点到其他点的距离和为 d e p [ i ] dep[i] dep[i]。对于某一个 i i i的子节点 j j j来说,显然 d e p [ i ] dep[i] dep[i]与 d e p [ j ] dep[j] dep[j]之间只有 i i i到 j j j的路统计数量不同(对于j的子树,无论在 i i i的子树中和在 j j j的子树中的统计数量肯定是一样的1,对于树中其他的点来说,这些点的循环次数也没有变化)且统计数量的差异就是少统计了 j j j的子树大小个,多统计了剩余节点个数个。设 j j j号点的子树大小为 s i z e [ j ] size[j] size[j]可推出DP方程式 d e p [ j ] = d e p [ i ] − s i z e [ i ] × l e n [ i ] [ j ] + ( n − s i z e [ j ] ) × l e n [ i ] [ j ] dep [j] = dep[i]-size[i] \times len[i][j] + (n - size[j]) \times len[i][j] dep[j]=dep[i]−size[i]×len[i][j]+(n−size[j])×len[i][j]
  因此可以先用dfs搜索出以节点 1 1 1为根的 d e p dep dep值再用刚才求出的DP方程式进行换根操作就可以求出答案了。

code

#include<cstdio>
#include<algorithm>
#define Size 100010

using namespace std;

struct line
{
	int to;
	int next;
	long long value;
}L[Size * 2];

int k[Size];

int n , top , hand[Size] , Siz[Size] , fat[Size] ;
long long dep[Size];

void add(int x , int y , long long z)
{
	L[top].to = y;
	L[top].next = hand[x];
	L[top].value = z;
	hand[x] = top ++;
}
//链式前向星

void size(int x , int last)
{
	Siz[x] = 1;
	fat[x] = last;
	for(int i = hand[x] ; i != -1 ; i = L[i].next)
	{
		int y = L[i].to;
		if(fat[x] == y)
			continue;
		size(y , x);
		Siz[x] += Siz[y];
		dep[1] += Siz[y] * L[i].value;
	}
}
//处理 1 根

void ans(int x)
{
	for(int i = hand[x] ; i != -1 ; i = L[i].next)
	{
		int y = L[i].to;
		if(fat[x] == y)
			continue;
		dep[y] = dep[x] + (n - Siz[y] - Siz[y]) * 1ll * L[i].value;
		ans(y);
	}
}
//换根

int main()
{
	for(int i = 0 ; i < Size ; i ++)
	{
		hand[i] = -1;
	}
	scanf("%d" , &n);
	for(int i = 0 ; i < n - 1 ; i ++ )
	{
		int x , y;
		long long z;
		scanf("%d%d%lld" , &x , &y , &z);
		add(x , y , z);
		add(y , x , z);
	}
	size(1 , 0);
	ans(1);
	for(int i = 1 ; i <= n ; i ++)
	{
		printf("%lld\n" , dep[i]);
	}
	return 0;
}

欢迎各路大佬吐槽……


  1. 可以画图看看。 ↩︎

上一篇:P3181 [HAOI2016]找相同字符(SAM的应用)


下一篇:省选测试8