[CSP-S模拟测试]:Park(树上DP)

题目描述

  公园里有$n$个雕像,有$n-1$条道路分别连接其中两个雕像,任意两个雕像可以直接或间接相连。现在每个景点$i$聚集着$P_i$只鸽子,旅行家手里有$v$数量的面包屑。
  一旦旅行家在雕像$i$撒下$1$单位面包屑,那么相邻的雕像的鸽子就都会飞到雕像$i$来觅食。
  时间线是这样的:首先,旅行家到达雕像$i$并与$P_i$鸽子会面。然后,他放下$1$单位面包屑。他离开雕像。在旅行家到达下一座雕像之前,来自相邻雕像的鸽子移动到雕像$i$(所以这些鸽子不计入他遇到的鸽子数)。注意旅行家每到一达雕像可以撒下面包屑,也可以不撒。
  旅行家可以在任何一座雕像上进入公园,沿着一些道路走下去(但不要使用同一道路两次),然后离开公园。
  在旅行家离开公园后,没有面包屑的小学生将进入并穿越完全相同的路线,并遇见许多群鸽子。
  通过最多$v$单位面包屑,旅行家希望最大化旅行家在路线上遇到的鸽子数量与小学生遇到的鸽子数量之间的差异。


输入格式

第一行两个整数$n,v$
第二行$n$个数为$P_i$
接下来$n-1$,每行两个整数,$X_i,Y_i$表示$X_i$与$Y_i$雕像相连


输出格式

输出只有一行,表示答案


样例

样例输入:

12 2
2 3 3 8 1 5 6 7 8 3 5 4
2 1
2 7
3 4  
4 7  
7 6  
5 6  
6 8  
6 9
7 10
10 11
10 12

样例输出:

36


数据范围与提示

样例解释:

一个可行的方案:旅行家从雕像$6$进入公园。在那里他遇到了$5$只鸽子。他放下面包屑。$p_6$现在是$27$,$p_5=p_7=p_8=p_9=0$。接下来,他跑向雕像$7$并遇到$0$鸽子。他撒下了第二个面包屑。$p_7=41$且$p_2=p_4=p_6=p_{10}=0$。他离开公园,他总共遇见$5+0=5$只鸽子。小学生跟着他走过同样的路线,但遇到$p_6+p_7=0+41=41$只鸽子。差是$41-5=36$。

数据范围:

对于$20\%$的数据,$1\leqslant n\leqslant 10$
对于另外$20\%$的数据,$1\leqslant n\leqslant 1,000$
对于另外$30\%$的数据,存在一个可行的最优方案从$1$出发
对于另外$30\%$的数据无特殊限制
对于$100\%$的数据,$1\leqslant n\leqslant 10^5,0\leqslant v\leqslant 100,0\leqslant P_i\leqslant 10^9$


题解

原题见$CEOI2017\ Chase$,我也不知道玄学出题人改的什么玄学题面……

一看就是$DP$题,但是显然我们不能枚举起点。

但是我们还是要先想一下枚举端点的做法,做事要慢慢来~

我的做法是设$dp[i][j][0/1]$表示到了$i$号点,已经撒了$j$次,在当前节点有没有撒的最大收益,转移是这样的:

$$dp[i][j][0]=max(dp[fa][j][0],dp[fa][j][1]) \\ dp[i][j][1]=max(dp[fa][j-1][0],dp[fa][j-1][1])+sum[i]-p[fa]$$

其中$sum[i]$表示在$i$号点撒会引来$sum[i]$只鸽子,不含$i$号点本身就有的,如下图$\downarrow$

$sum[i]$是指$\sum \limits_{i=1}^4 p_i$。

直接这样做会$TLE\ 44$,如果我们加上$clock()$的话可以得到$74\sim 78$不等。

那么我们现在考虑满分解法,如何快速求出以每个点为起点的最大贡献,将其取$\max$即可。

考虑定义两个数组$up[i][j]$和$down[i][j]$分别表示从$i$的子树走向$i$和从$i$走向$i$的子树,撒了$j$次的最大贡献。

然后状态转移是这样的:

$$up[i][j]=\max(up[i][j],up[son][j],up[son][j-1]+sum[i]-p[son]) \\ down[i][j]=\max(down[i][j],down[son][j],down[son][j-1]+sum[i]-p[fa])$$

还需要注意一点,如下图$\downarrow$

两种走法的最大贡献不一定相等,于是我们要正序枚举一遍所有的儿子,再倒着枚举一遍即可。

时间复杂度:$\Theta(n\times v)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,v;
int p[100001];
long long sum[100001];
long long dp[2][100001][101];
long long ans;
vector<int> e[100001];
void dfs(int x,int fa)
{
	for(int i=1;i<=v;i++)
	{
		dp[0][x][i]=sum[x];
		dp[1][x][i]=sum[x]-p[fa];
	}
	for(int i=0;i<e[x].size();i++)
		if(e[x][i]!=fa)
		{
			dfs(e[x][i],x);
			for(int j=1;j<v;j++)ans=max(ans,dp[0][x][j]+dp[1][e[x][i]][v-j]);
			for(int j=1;j<=v;j++)
			{
				dp[0][x][j]=max(dp[0][x][j],max(dp[0][e[x][i]][j],dp[0][e[x][i]][j-1]+sum[x]-p[e[x][i]]));
				dp[1][x][j]=max(dp[1][x][j],max(dp[1][e[x][i]][j],dp[1][e[x][i]][j-1]+sum[x]-p[fa]));
			}
		}
	reverse(e[x].begin(),e[x].end());
	for(int i=1;i<=v;i++)
	{
		dp[0][x][i]=sum[x];
		dp[1][x][i]=sum[x]-p[fa];
	}
	for(int i=0;i<e[x].size();i++)
		if(e[x][i]!=fa)
		{
			for(int j=1;j<v;j++)ans=max(ans,dp[0][x][j]+dp[1][e[x][i]][v-j]);
			for(int j=1;j<=v;j++)
			{
				dp[0][x][j]=max(dp[0][x][j],max(dp[0][e[x][i]][j],dp[0][e[x][i]][j-1]+sum[x]-p[e[x][i]]));
				dp[1][x][j]=max(dp[1][x][j],max(dp[1][e[x][i]][j],dp[1][e[x][i]][j-1]+sum[x]-p[fa]));
			}
		}
	ans=max(ans,max(dp[0][x][v],dp[1][x][v]));
}
int main()
{
	scanf("%d%d",&n,&v);if(!v){puts("0");return 0;}
	for(int i=1;i<=n;i++)scanf("%d",&p[i]);
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		e[x].push_back(y);
		e[y].push_back(x);
		sum[x]+=p[y];
		sum[y]+=p[x];
	}
	dfs(1,0);
	printf("%lld",ans);
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11631908.html

时间: 2024-08-09 14:35:47

[CSP-S模拟测试]:Park(树上DP)的相关文章

[CSP-S模拟测试]:折射(DP)

题目描述 小$Y$十分喜爱光学相关的问题,一天他正在研究折射. 他在平面上放置了$n$个折射装置,希望利用这些装置画出美丽的折线. 折线将从某个装置出发,并且在经过一处装置时可以转向,若经过的装置坐标依次为$(x_1,y_1),(x_2,y_2),...(x_k,y_k)$,则必须满足: $\bullet \forall j\in (1,k],y_j<y_{j−1}$ $\bullet \forall j\in (2,k],x_{j−2}<x_j<x_{j−1}or\ x_{j−1}&l

[CSP-S模拟测试]:tree(DP)

题目传送门(内部题57) 输入格式 第一行包含一个数:$n$表示树的节点数.接下来$n-1$行,每行包含两个数:$u,v$表示无根树的一条边. 输出格式 输出$n$行,第$i$行包含一个浮点数,保留三位小数,表示第$i$号点第一次访问的期望时间. 样例 样例输入: 31 22 3 样例输出: 1.0002.0005.000 数据范围与提示 样例解释: 样例解释:容易分析出,所有可能情况下,到达$1$号点和$2$号点的时间都分别是:$1$和$2$,我们考虑$3$号点的到达时间,所有可能的过程:$1

[CSP-S模拟测试]:题(DP)

题目描述 由于出题人赶时间所以没办法编故事来作为背景.一开始有$n$个苹果,$m$个人依次来吃苹果,第$i$个人会尝试吃$u_i$或$v_i$号苹果,具体来说分三种情况.$\bullet 1.$两个苹果都还在,那么这个人将随便选一个苹果吃了.$\bullet 2.$只有一个苹果,那么这个人将吃掉这个苹果.$\bullet 3.$都不在了,这个人吃不到苹果就走了.请问有多少对苹果$(i,j)(i<j)$满足它们两个都幸存下来的概率$>0$. 输入格式 第一行两个数$n,m$.接下来$m$行,每行

[CSP-S模拟测试]:花(DP)

题目传送门(内部题111) 输入格式 一个整数$T$,表示测试数据组数. 每组测试数据占一行,两个整数,分别表示$L$和$S$. 输出格式 对每组数据,输出一个整数表示答案. 样例 样例输入1: 13 7 样例输出1: 7 样例输入2: 24 210 11 样例输出2: 4410199993 数据范围与提示 样例$1$解释: 一共有$7$种形态,每种形态能构成$1$个方案. 样例$2$解释: AAAB ABBB BAAA BBBA 数据范围: 对于$60\%$的数据,$L\leqslant 30

[CSP-S模拟测试]:蛇(DP+构造+哈希)

题目传送门(内部题140) 输入格式 前两行有两个长度相同的字符串,描述林先森花园上的字母. 第三行一个字符串$S$. 输出格式 输出一行一个整数,表示有多少种可能的蛇,对$10^9+7$取模. 样例 样例输入1: rwbyybwrrwby 样例输出1: 4 样例输入2: oooooooo 样例输出2: 14 数据范围与提示 对于$20\%$的数据,$n,|S|\leqslant 16$. 对于$40\%$的数据,$n,|S|\leqslant 40$. 对于$60\%$的数据,$n,|S|\l

2018冬令营模拟测试赛(三)

2018冬令营模拟测试赛(三) [Problem A]摧毁图状树 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见"试题描述" 数据规模及约定 见"试题描述" 题解 这题没想到贪心 QwQ,那就没戏了-- 贪心就是每次选择一个最深的且没有被覆盖的点向上覆盖 \(k\) 层,因为这个"最深的没有被覆盖的点"不可能再有其它点引出的链覆盖它了,而它又

2018冬令营模拟测试赛(十七)

2018冬令营模拟测试赛(十七) [Problem A]Tree 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见"试题描述" 数据规模及约定 见"试题描述" 题解 这个数据范围肯定是树上背包了. 令 \(f(i, j, k)\) 表示子树 \(i\) 中选择了 \(j\) 个节点,路径与根的连接情况为 \(k\),具体地: \(k = 0\) 时,路径的两个端点

模拟测试68,69

68: 32 AlpaCa 41 03:08:20 31 02:46:16 30 02:46:28 102 03:08:20 69: 28 AlpaCa 20 02:51:15 60 03:05:32 0 01:39:45 80 03:05:32 彻底挂掉了呢. 不过也还好吧,至少之后的考试不会有那么大压力了吧(出第一机房是肯定的事了),那利用之后几场考试就调整好状态,下次再来嘛. 但是最近炸了那么多场还是要反思一下,其实不只是心态的问题,自身实力,考试技巧,时间分配等方面好像都有点问题. 69

模拟测试(vj)

做这份模拟测试,已经崩溃了,英文看不懂,题意理解错.到结束了只a了第一题,人生陷入了低谷,于是花了一天的时间终于把不会的弄明白了,在这里写一份总结~ T1,简单的模拟,如果打枪打中一支鸟,将这个位置设为0,并向两边扩散,注意这个位置一定要有鸟. 代码~ #include<bits/stdc++.h> using namespace std; int a[30000]; int n,m; int main() { cin>>n; for(int i=1;i<=n;i++) ci