牛客-3566B-小琛和他的学校-树形dp

题目描述:

链接:https://ac.nowcoder.com/acm/contest/3566/B
来源:牛客网

小琛是一所学校的校长。
他的学校有n个校区(编号1~n),被n-1条双向道路连接,呈树形结构。
第i个校区共有Ai个学生。
第i天早上,所有的学生会沿最短路走到第i个校区参加活动,晚上再原路返回。
一个人通过第j条通道一次(即一人次),需要小琛支付wj的维护费用。
小琛想知道第n天结束之后,对于每一条通道,他总共需要支付多少费用。
对于100%的数据,1≤ n ≤ 200,000,1≤ A[i]≤ 10,000,1≤ w[i] ≤ 10,000。

输入描述:

第一行一个整数n,表示校区的数量。
接下来一行,n个整数,表示A1~An。
第3到第n+1行,每行包含3个整数。第i行包含三个整数ui-2,vi-2,wi-2,表示第i-2条通道所连接的两个校区的编号,以及一人次通过这条通道的费用。

输出描述:

共n-1行,每行一个整数。
第i行的整数表示小琛对于第i条通道所需支付的费用。

输入样例:

4
2 1 2 3
1 3 1
1 2 3
4 1 2

输出样例:

24
60
56

核心思想:

树形dp。dfs返回子树的人数和点数。
路(a,b)的费用=(子树a的人数*子树b的点数+子树a的点数*子树b的人数)*2

代码如下:

#include
#include
using namespace std;
typedef long long ll;
const int N=2e5+20;
int n,a[N],head[N],cs;
ll pep,ans[N];
struct node{
	int y,v,ne;
}side[N<<1];
struct Node{
	ll ren,dian;
	Node(){
	}
	Node(ll x,ll y)
	{
		ren=x;
		dian=y;
	}
};
void add(int x,int y,int v)
{
	side[cs].y=y;
	side[cs].v=v;
	side[cs].ne=head[x];
	head[x]=cs++;
	return;
}
Node dfs(int x,int fa)
{
	Node re=Node(a[x],1);
	for(int i=head[x];i!=-1;i=side[i].ne)
	{
		int y=side[i].y;
		int v=side[i].v;
		if(y==fa)continue;
		Node t=dfs(y,x);
		ans[i>>1]+=(t.ren*(n-t.dian)+t.dian*(pep-t.ren))*v*2;
		re.ren+=t.ren;
		re.dian+=t.dian;
	}
	return re;
}
int main()
{
	int x,y,v;
	cin>>n;
	pep=0;
	for(int i=0;i<N;i++)
		head[i]=-1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		pep+=a[i];
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	dfs(1,-1);
	int en=n-1;
	for(int i=0;i<en;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

你可能感兴趣的:(动态规划)