[洛谷-P3047] [USACO12FEB]Nearby Cows G(树形DP+换根DP)

2024-02-29 01:59

本文主要是介绍[洛谷-P3047] [USACO12FEB]Nearby Cows G(树形DP+换根DP),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[洛谷-P3047] [USACO12FEB]Nearby Cows G

  • 一、问题
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
  • 二、分析
    • 1、状态表示
    • 2、状态转移
    • 3、换根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.

输入格式

* 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,k n,k
接下来 n − 1 n-1 n1 行,每行两个正整数 u , v u,v u,v,表示 u , v u,v u,v 之间有一条边。
最后 n n n 行,每行一个非负整数 c i c_i ci,表示点权。

输出格式

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

输出 n n n 行,第 i i i 行一个整数表示 m i m_i mi

样例 #1

样例输入 #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\% 100% 的数据: 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 1 ≤ k ≤ 20 1 \le k \le 20 1k20 0 ≤ c i ≤ 1000 0 \le c_i \le 1000 0ci1000

二、分析

题目简单地来说就是:
给你一棵 n n n 个点的树,点带权,对于每个节点求出距离它不超过 k k k 的所有节点权值和 m i m_i mi

对于树中的某个节点而言,距离它不超过 k k k的节点主要来源于两方面,一个是该节点的子节点中距离该节点不超过距离 k k k的节点的权值和,另一个就是该节点向上沿着父节点方向不超过距离 k k k的点的权值和。

对于子节点方向的节点的权值和,我们可以先通过普通的树形DP计算出来。

因此,我们先写一个DP计算出子树中距离该点不超过 k k k的点的权值和。

1、状态表示

f [ u ] [ k ] f[u][k] f[u][k]表示以 u u u为根节点的树中,距离 u u u不超过 k k k的子节点的权值和。

2、状态转移

f [ u ] [ k ] = v a l [ u ] + ∑ f [ s o n ] [ k − 1 ] f[u][k]=val[u]+\sum f[son][k-1] f[u][k]=val[u]+f[son][k1]
到节点 u u u不超过距离 k k k,即距离 s o n son son不超过 k − 1 k-1 k1,然后加在一起即可。同时 u u u节点本身也是答案,因为 u u u节点本身是不超过距离 0 0 0的节点。

3、换根DP

这个题目本身是个无根树,如果我们认为规定编号为1的节点是根的话,那么对于祖宗节点 1 1 1来说, f [ 1 ] [ k ] f[1][k] f[1][k]就是距离 1 1 1节点不超过距离 k k k的节点的权值和。因为祖宗节点是没有父亲节点的,所以我们就不需要考虑沿着父节点方向的节点权值和。

我们不妨令: g [ u ] [ k ] g[u][k] g[u][k]表示所有到 u u u节点的不超过距离 k k k的节点的权值和。根据刚刚的分析: g [ 1 ] [ k ] = f [ 1 ] [ k ] g[1][k]=f[1][k] g[1][k]=f[1][k]

这个就是我们换根DP的初始化。其实受这个的启发,我们完全可以去把每个点都当作根,然后暴力跑出答案,但是这个暴力做法的时间复杂度是 O ( n 2 ) O(n^2) O(n2)的,会超时。

所以当我们将祖宗节点从节点1换为另一个节点的时候,我们只能通过数学上的关系来计算出 g g g数组元素的值。这个也是换根DP的意义。

我们看下面的图:
在这里插入图片描述
红色框是非常好理解的,直接写成 f [ u ] [ k ] f[u][k] f[u][k]即可。上面的部分,我们可以写成 g [ f a ( u ) ] [ k − 1 ] g[fa(u)][k-1] g[fa(u)][k1]。因为到 u u u不超过 k k k的距离,即距离它的父亲节点不超过 k − 1 k-1 k1的距离。

但是这么写对吗?

答案是不对的, g [ f a ( u ) ] [ k − 1 ] g[fa(u)][k-1] g[fa(u)][k1] f [ u ] [ k ] f[u][k] f[u][k]是有重复部分的。我们需要减去这段重复的部分,那么关键问题是重复部分如何表示?

重复部分肯定是出现在了红色框中,红色框中到 f a ( u ) fa(u) fa(u)不超过距离 k − 1 k-1 k1,即距离 u u u不超过 k − 2 k-2 k2,同时重复部分又恰好是节点 u u u的子节点,所以这部分可以表示为: f [ u ] [ k − 2 ] f[u][k-2] f[u][k2]

所以最终的结果就是:

g [ u ] [ k ] = f [ u ] [ k ] + g [ f a ( u ) ] [ k − 1 ] − f [ u ] [ k − 2 ] g[u][k]=f[u][k]+g[fa(u)][k-1]-f[u][k-2] g[u][k]=f[u][k]+g[fa(u)][k1]f[u][k2]

但是上述方程成立的条件是 k ≥ 2 k\geq 2 k2的。

所以我们还得想一想 k ≤ 1 k \leq 1 k1的时候。

如果 k = 0 k=0 k=0 g [ u ] [ 0 ] g[u][0] g[u][0]其实就是 v a l [ u ] val[u] val[u],因为不超过距离 0 0 0的点只有本身。

如果 k = 1 k=1 k=1,那么 g [ u ] [ 1 ] g[u][1] g[u][1]其实就是 f [ u ] [ 1 ] + v a l [ f a ( u ) ] f[u][1]+val[fa(u)] f[u][1]+val[fa(u)],因为沿着父节点方向距离 u u u不超过 1 1 1的点,只有父节点,而树中,父节点是唯一的。沿着子节点方向,其实就是 u u u的各个子节点,而这些子节点可以统统用 f [ u ] [ 1 ] f[u][1] f[u][1]表示。

三、代码

#include<bits/stdc++.h>
#define endl '\n'
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 10;
vector<int>edge[N];
int f[N][25], g[N][25];
int val[N];
int n, k;void dp(int u, int father)
{for(int i = 0; i <= k; i ++)f[u][i] = val[u];for(int i = 0; i < edge[u].size(); i ++ ){int son = edge[u][i];if(son == father)continue;dp(son, u);for(int j = 1; j <= k; j ++ ){f[u][j] += f[son][j - 1];}}return;
}void dp2(int u, int father)
{for(int i = 0; i < edge[u].size(); i ++ ){int son = edge[u][i];if(son == father)continue;g[son][0] = val[son];g[son][1] = f[son][1] + val[u];for(int j = 2; j <= k; j ++ ){g[son][j] = g[u][j - 1] + f[son][j] - f[son][j - 2]; }dp2(son, u);}
}void solve()
{cin >> n >> k;for(int i = 0; i < n - 1; i ++ ){int a, b;cin >> a >> b;edge[a].push_back(b);edge[b].push_back(a);}for(int i = 1; i <= n; i ++ )cin >> val[i];dp(1, 0);for(int i = 0; i <= k; i ++ ){g[1][i] = f[1][i];}dp2(1, 0);// for(int i = 1; i <= n; i ++ )// {// 	cout << f[i][k] << endl;// }// cout << endl;for(int i = 1; i <= n; i ++ ){cout << g[i][k] << endl;}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);solve();
}

这篇关于[洛谷-P3047] [USACO12FEB]Nearby Cows G(树形DP+换根DP)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/757257

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.2 Milking Cows(类hash表)

第一种思路被卡了时间 到第二种思路的时候就觉得第一种思路太坑爹了 代码又长又臭还超时!! 第一种思路:我不知道为什么最后一组数据会被卡 超时超了0.2s左右 大概想法是 快排加一个遍历 先将开始时间按升序排好 然后开始遍历比较 1 若 下一个开始beg[i] 小于 tem_end 则说明本组数据与上组数据是在连续的一个区间 取max( ed[i],tem_end ) 2 反之 这个

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl

uva 10029 HASH + DP

题意: 给一个字典,里面有好多单词。单词可以由增加、删除、变换,变成另一个单词,问能变换的最长单词长度。 解析: HASH+dp 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o