P3047 [USACO12FEB] Nearby Cows G(树形换根dp )

9 篇文章 0 订阅
6 篇文章 0 订阅
文章描述了一个编程问题,给定一棵带权树和一个整数k,要求对每个节点计算其所有距离不超过k的节点权值和。使用深度优先搜索(DFS)和动态规划方法(dp1和dp2)来解决,通过两次dfs操作和容斥原理优化计算过程,时间复杂度为O(n*k)。
摘要由CSDN通过智能技术生成

给你一棵 n 个点的树,点带权,对于每个节点求出距离它不超过 k 的所有节点权值和 mi​。

第一行两个正整数 n,k。
接下来 n−1 行,每行两个正整数 u,v,表示 u,v 之间有一条边。
最后 n 行,每行一个非负整数 ci​,表示点权。

输出格式

* Lines 1..N: Line i should contain the value of M(i).

输出 n 行,第 i 行一个整数表示 mi​。

输入输出样例

输入 #1复制

6 2 
5 1 
3 6 
2 4 
2 1 
3 2 
1 
2 
3 
4 
5 
6 

输出 #1复制

15 
21 
16 
10 
8 
11 

说明/提示

There are 6 fields, with trails connecting (5,1), (3,6), (2,4), (2,1), and (3,2). Field i has C(i) = i cows.

Field 1 has M(1) = 15 cows within a distance of 2 trails, etc.

【数据范围】
对于 100%100% 的数据:1≤n≤105,1≤k≤20,0≤ci​≤1000

解析:

第一次dfs,先从根节点为1时,开始遍历的的子树,记录它的子树。

第二次dfs,进行根的转换,记录其总和。

利用容斥原理,避免多次相加同一个数,这时的   dp2[v][j] += dp2[x][j-1] - dp1[v][j-2]; 的含义就出来了,dp2[v][i]因为根节点处理好了,所以直接处理子节点。利用根节点转换。

dp2[x][j-1] 表示的是因为x 距离子节点v为1距离,所以我们应该得到的是以x为中心,距离为小于等于 j-1的那些点。

dp1[v][j-2]是以v为中心距离,以v为根,距离为j-2那些点,最后删去,得到的就是以v为中心距离为小于等于 j 的哪些点

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n,val[N],k;

vector<int> g[N];

int dp1[N][22],dp2[N][22];

void dfs1(int x,int fa)
{
	for(int i = 0;i <= k;i++)
	{// dp1[i][j] 表示 在以 i为 根 ,距离不超过 j 的 总和 
		dp1[x][i] = val[x];
	}
	for(int i =0;i < g[x].size();i++)
	{
		int v = g[x][i];
		if(v != fa)
		{
			dfs1(v,x);
			for(int j = 1;j <= k;j++)
			{
				dp1[x][j] += dp1[v][j-1];// 只是记录下面节点的 
			}
		}
	}
	return;
}

void dfs2(int x,int fa)
{
	for(int i= 0;i < g[x].size();i++)
	{
		int v = g[x][i];
		if(v!= fa)
		{
			dp2[v][1] += dp1[x][0];
			for(int j = 2;j <= k;j++)
			{
				dp2[v][j] += dp2[x][j-1] - dp1[v][j-2];// dp1 是以 v为根距离为 j-2的那些点 
				// dp2[x][j-1] 减去根节点的差距 
			}
			dfs2(v,x);
		}
	}
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i = 1;i < n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i = 1;i <= n;i++)
	{
		scanf("%d",&val[i]);
	}
	dfs1(1,0);
	for(int i = 1;i <= n;i++)
	{
		for(int j = 0;j <= k;j++)
		{
			dp2[i][j] = dp1[i][j];
		}
	}
	dfs2(1,0);
	for(int i = 1;i <= n;i++)
	{
		cout << dp2[i][k]<<'\n';
	}
	return 0; 
}

时间复杂度为:O(n*k);

总结:

在遍历 每颗树形结构是,可以先从1到 叶子节点 ,是dfs后在处理父节点信息。

在判断其父节点和子树直接的关系时,是父节点处理儿子节点,在进行dfs()下个儿子节点 遍历。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值