“卓见杯”第五届CCPC中国大学生程序设计竞赛河南省赛网络赛 D Defending Plan Support(树形dp,贡献值,递推)

题目描述

The architectural structure of the college is strange, but the rule is that there is only one simple path between every two classrooms. Now the battle between Class A and Class F broke out. As a support staff of Class F, you have to go to every fight in time to help out. With the help of icebound, Class F know the probability of each classroom being attacked. So we define an important degree for each classroom. Now ask you to find a classroom x in n classrooms as your resting base which has the minimum F(x)F(x) = ∑ w(i) × d(x, i)d is the distance between x and i.

输入

he first line is an integer n,which is the number of classrooms.
Then n-1 lines follow. Each line has three numbers x,y,z. There is a road of meters between x and y.
The last line contains n numbers. The i‑th number w(i) is the important degree of the classroom.
2 ≤ n ≤ 5 × 105, 0 ≤ z, wi ≤ 1000,1 ≤ x, y ≤ n

输出

Output a line with an integer, representing the minimum F(x).

样例输入 Copy

5
1 2 1
2 3 1
2 4 1
3 5 6
2 3 1 8 7

样例输出 Copy

60

题意:给出一个树,有边权,有节点的重量,让你求g[x]的最小值,g[x] = ∑ ( w[i] * d(i,x) ) 。w[i] 为除x外其他一些节点重量,d(i,x)  为 i 到 x 的距离。

思路:说实话,比赛时,不会写,队友强,用树形dp,递推 贡献值。

树形dp. 首先随机选取一个根, 然后预处理出f[i]表示i的子树上所有点的权值和, g[i]表示i的整颗子树对当前i的贡献

转移 f[i]=w[i]+∑j是i的儿子 f[j]f[i]=w[i]+∑j是i的儿子 f[j] , g[i]=∑j是i的儿子 (g[j]+f[j]∗dis(i,j))g[i]=∑j是i的儿子 (g[j]+f[j]∗dis(i,j))

其中 w[i] 表示i点的权值

然后从根再做一次dfs, 不断将父亲节点当做子节点的子树并将贡献向下传递给子节点即可

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define Max 500010 
int w[Max],f[Max];
bool book[Max]; 
ll g[Max];
struct node{
	int y,z;
};
vector<node> v[Max];

int dfs(int u)
{
	book[u] = true;
	int sum = w[u];
	for(int i = 0;i<v[u].size();i++){
		node tt = v[u][i];
		int v = tt.y;
		if(!book[v]){
			int k = dfs(v);
			sum += k;
			g[u] += g[v] + (ll) k * tt.z; 
		}
	}
	return f[u] = sum;
}

ll dfs1(int u,int fw,ll fg)  // 节点u,fw为当初以1为根节点的树,u为子树时,除u为子树的结点重量和之外的所有结点重量之和 
							// fg 为 fw 对 节点u的贡献值。 
{
	ll Ma = g[u]+fg;
	book[u] = true;
	for(int i = 0;i<v[u].size();i++){
		node tt = v[u][i];
		int v = tt.y;
		if(!book[v]){
			Ma = min(Ma,dfs1(v,fw + f[u]-f[v],fg + g[u]-g[v]-(ll)f[v]*tt.z +(ll)(fw+f[u]-f[v])*tt.z));
		}  // fg + g[u]-g[v]-(ll)(f[v]*tt.z) 为当初以1为根节点时,v为根节点的子树 时,除v为根节点的子树和 u本身节点外,所有节点对
			// 所有节点对u的贡献值;(fw+f[u]-f[v])*tt.z))  (fw+f[u]-f[v])为除 v 为根节点子树外的所有权值。乘于tt.z 只是这一段的权重。 
	}   // 就是运用的 g[i]的递推公式。这个是从根节点往下扫一边,dfs1主要是为了求除当前节点为子树外的所有节点,对当前节点的贡献值。 
	return Ma;
}
int main()
{
	int n;
	int x,y,z;
	scanf("%d",&n);
	for(int i = 1;i<n;i++){
		scanf("%d%d%d",&x,&y,&z);
		//G[u].push_back(v*1005+w);   如果爆内存了,小技巧,可以这样压缩一下,看数据范围压缩。 
        //G[v].push_back(u*1005+w);   或者把w[] 和 f[]合为一个数组。 
		node tt;
		tt.y = y;
		tt.z = z;
		v[x].push_back(tt);
		tt.y = x;
		v[y].push_back(tt);
	}
	for(int i = 1;i<=n;i++)
		scanf("%d",&w[i]);
	dfs(1);	
	memset(book,false,(n+1)*sizeof(bool));
	printf("%llu \n",dfs1(1,0,0));        
	return 0;
} 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值