[洛谷]P3047 [USACO12FEB]附近的牛Nearby Cows (#树形dp)

题目描述

Farmer John has noticed that his cows often move between nearby fields. Taking this into account, he wants to plant enough grass in each of his fields not only for the cows situated initially in that field, but also for cows visiting from nearby fields.

Specifically, FJ's farm consists of N fields (1 <= N <= 100,000), where some pairs of fields are connected with bi-directional trails (N-1 of them in total). FJ has designed the farm so that between any two fields i and j, there is a unique path made up of trails connecting between i and j. Field i is home to C(i) cows, although cows sometimes move to a different field by crossing up to K trails (1 <= K <= 20).

FJ wants to plant enough grass in each field i to feed the maximum number of cows, M(i), that could possibly end up in that field -- that is, the number of cows that can potentially reach field i by following at most K trails. Given the structure of FJ's farm and the value of C(i) for each field i, please help FJ compute M(i) for every field i.

农民约翰已经注意到他的奶牛经常在附近的田野之间移动。考虑到这一点,他想在每一块土地上种上足够的草,不仅是为了最初在这片土地上的奶牛,而且是为了从附近的田地里去吃草的奶牛。

具体来说,FJ的农场由N块田野构成(1 <= n <= 100,000),每两块田野之间有一条无向边连接(总共n-1条边)。FJ设计了农场,任何两个田野i和j之间,有且只有一条路径连接i和j。第 i块田野是C(i)头牛的住所,尽管奶牛们有时会通过k条路到达其他不同的田野(1<=k<=20)。

FJ想在每块田野上种上够M(i)头奶牛吃的草。M(i)指能从其他点经过最多k步就能到达这个点的奶牛的个数。

现给出FJ的每一个田野的奶牛的数目,请帮助FJ计算每一块田野的M(i)。

输入格式

* Line 1: Two space-separated integers, N and K.

* Lines 2..N: Each line contains two space-separated integers, i and j (1 <= i,j <= N) indicating that fields i and j are directly connected by a trail.

* Lines N+1..2N: Line N+i contains the integer C(i). (0 <= C(i) <= 1000)

第一行:n和k;

后面n-1行:i和j(两块田野);

之后n行:1..n每一块的C(i);

输出格式

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

n行:每行M(i);//i:1..2

输入输出样例

输入 #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.

题目简述:给出一棵n个点的树,每个点上有C_i头牛,问每个点k步范围内各有多少头牛。

感谢@Slager_Z 提供翻译


思路

给出一棵n个点的树,每个点上有Ci头牛,问每个点k步范围内各有多少头牛。

令dp[i][j]为节点i向外走j步能覆盖的奶牛数目。对于这题而言,显然是个树形dp,而我们的一般处理方法是在节点i的子树中合并。然而这题不一样,答案不只是从子树中转移过来,所以最后一定能得到最后2个关于dp[i][j]的方程:

1.从儿子到父亲;

2.从父亲到儿子。

1.从儿子到父亲

显然地,

dp[i][j]=Σdp[k][j-1]

其中k是i的子节点。转移很简单,一个dfs搞定的事情。

2.从父亲到儿子

dp[k][j]=Σdp[i][j-1]

其中i是k的父节点。也可以dfs遍历一遍。

如果真的按照那个方程去写的话,你会发现只有第1个是对的,剩下的总是多一点,应该是有重复了。因为到节点i的距离为2的点包含到节点k距离为1的节点k的儿子们,那么重算的一部分应该是dp[i][j-2]。所以要减去。

即最后的方程为:

dp[i][j]=Σdp[k][j-1]

dp[k][j]=Σdp[i][j-1]-dp[i][j-2]

#include <stdio.h>
#include <iostream>
#define N 100001
using namespace std;
int n,m,cnt,head[N],f[N],dp[N][21],s;
struct node
{
	int to,nxt;
}e[N<<1];
inline void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa)
{
	register int i,j;
	for(i=head[u];i;i=e[i].nxt)
	{
		int v(e[i].to);
		if(v==fa) continue;
		dfs(v,u);
		for(j=1;j<=m;j++)
		{
			dp[u][j]+=dp[v][j-1];
		}
	}
}
void dfs1(int u,int fa)
{
	register int i,j;
	for(i=head[u];i;i=e[i].nxt)
	{
		int v(e[i].to);
		if(v==fa) continue;
		/*dp[v][1]+=dp[u][0];
		for(j=2;j<=m;j++)
		{
			dp[v][j]+=dp[u][j-1]-dp[v][j-2];
		}*/
		for(j=m;j>=2;j--)
		{
			dp[v][j]+=dp[u][j-1]-dp[v][j-2];
		}
		dp[v][1]+=dp[u][0];
		dfs1(v,u);
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	register int i,j,k;
	cin>>n>>m;
	for(i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(i=1;i<=n;i++)
	{
		cin>>dp[i][0];
	}
	dfs(1,0);
	dfs1(1,0);
	for(i=1;i<=n;i++)
	{
		s=0;
		for(j=0;j<=m;j++)
		{
			s+=dp[i][j];
		}
		cout<<s<<endl;
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值