点此看题面

大致题意: 有一棵树,树上编号为\(i\)的节点上有\(F_i\)个铁球,逃亡者有\(V\)个磁铁,当他在某个节点放下磁铁时,与这个节点相邻的所有节点上的铁球都会被吸引到这个节点。然后一个追逐者会顺着同样的路去追逐逃亡者。问追逐者遇到的铁球数减去逃亡者遇到的铁球数的最大值。

一个暴力\(DP\)

我们先来考虑一个暴力的树形\(DP\)

不难发现,经过一个节点所能得到的收益应该是它的所有子节点的权值之和,即:$$f_{x,i}=max(f_{fa_x,i},f_{fa_x,i-1}+Size_x)$$

其中\(f_{x,i}\)表示从以某一节点为根节点出发(根节点可以拿来枚举毕竟这是暴力的做法)到达编号为\(x\)的节点,放下\(i\)个磁铁所能到达的最大收益。而\(Size_x\)则表示\(x\)的所有子节点的铁球数量之和

代码如下:

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
#define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
#define N 100000
#define M 100
LL pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
using namespace std;
LL n,m,ee=0,res,a[N+5],lnk[N+5],fa[N+5],Size[N+5],f[N+5][M+5];
struct edge
{
LL to,nxt;
}e[(N<<1)+5];
inline void read(LL &x)
{
x=0;LL f=1;static char ch;
while(!isdigit(ch=tc())) f=ch^'-'?1:-1;
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
x*=f;
}
inline void write(LL x)
{
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
inline void add(LL x,LL y)//新加上一条边
{
e[++ee].to=y,e[ee].nxt=lnk[x],lnk[x]=ee,e[++ee].to=x,e[ee].nxt=lnk[y],lnk[y]=ee;
}
inline void GetRt(LL x)//以一个新的节点为根,因此要重新遍历树上的每个节点,并更新每个节点的信息
{
Size[x]=0;//现将当前节点的子节点铁球数量和清零
for(register LL i=lnk[x];i;i=e[i].nxt)//枚举当前节点的每一个子节点
if(e[i].to^fa[x]) fa[e[i].to]=x,GetRt(e[i].to),Size[x]+=a[e[i].to];//更新子节点的信息,并计算出当前节点子节点的铁球数量和
}
inline void DP(LL x)//树形DP,计算出对于每个节点的答案
{
register LL i;
for(res=max(res,f[x][0]=f[fa[x]][0]),i=1;i<=m;++i) f[x][i]=max(f[fa[x]][i],f[fa[x]][i-1]+Size[x]),res=max(res,f[x][i]);//DP转移,并用res记录f数组最大值
for(i=lnk[x];i;i=e[i].nxt) if(e[i].to^fa[x]) DP(e[i].to);//对当前节点的每一个子节点进行DP
}
inline void Clear(int x)//换一个新的根节点,清空数组
{
register LL i,j;
for(fa[x]=res=i=0;i<=n;++i)
for(j=0;j<=m;++j) f[i][j]=0;
}
inline void GetAns(LL x)//求出以x为根节点的答案
{
register LL i;
for(Clear(x),GetRt(x),f[x][1]=Size[x],i=lnk[x];i;i=e[i].nxt) DP(e[i].to);//访问根节点的每一个子节点,对其进行树形DP
}
int main()
{
register LL i,ans=0;
for(read(n),read(m),i=1;i<=n;++i) read(a[i]);
for(i=1;i<n;++i)
{
static LL x,y;
read(x),read(y),add(x,y);
}
for(i=1;i<=n;++i) GetAns(i),ans=max(ans,res);//枚举每一个节点作为根节点,并求出对应的答案
return write(ans),fwrite(pp,1,pp_,stdout),0;
}

考虑优化

不难发现,上面这个\(DP\)的时间复杂度是\(O(n^2m)\)的(枚举根节点是\(O(n)\)的,\(DP\)是\(O(nm)\)的),显然会\(TLE\),因此我们要考虑优化。

应该可以发现,\(DP\)的时间复杂度是难以优化的,因此我们就要想想看能不能不枚举根节点直接\(DP\)。

首先,我们以1号节点为根。

然后,我们可以用\(Up_{i,j}\)来表示\(i\)的子树中的某个节点走到\(i\),放下\(j\)个磁铁所能得到的最大收益,并用\(Down_{i,j}\)来表示\(i\)走到\(i\)的子树中的某个节点,放下\(j\)个磁铁所能得到的最大收益。

然后,就可以得出转移方程:

\[Up_{x,i}=max(Up_{x,i},max(Up_{son,i},Up_{son,i-1}+sum_x-a_{son}))
\]

\[Down_{x,i}=max(Down_{x,i},max(Down_{son,i},Down_{son,i-1}+sum_x-a_{fa}))
\]

代码如下:

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define LL unsigned long long
#define N 100000
#define M 100
#define add(x,y) (e[++ee].to=y,e[ee].nxt=lnk[x],lnk[x]=ee,sum[x]+=a[y])
char ff[10000000],*A=ff;
using namespace std;
LL n,m,ans,ee=0,top=0,a[N+5],lnk[N+5],sum[N+5],Up[N+5][M+5],Down[N+5][M+5],Stack[N+5];
struct edge
{
LL to,nxt;
}e[(N<<1)+5];
inline void read(LL &x)
{
x=0;static char ch;
while(!isdigit(ch=*A++));
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=*A++));
}
inline void write(LL x)
{
if(x>9) write(x/10);
putchar(x%10+48);
}
inline void DP(LL x,LL son,LL fa)//对节点x进行树形DP,其中son为该节点的一个子节点,fa为该节点的父亲节点
{
register LL i;
for(i=1;i<=m;++i) ans=max(ans,Up[x][i]+Down[son][m-i]);//先记录答案,然后再更新,不然会出现重复计算
for(i=1;i<=m;++i) Up[x][i]=max(Up[x][i],max(Up[son][i],Up[son][i-1]+sum[x]-a[son])),Down[x][i]=max(Down[x][i],max(Down[son][i],Down[son][i-1]+sum[x]-a[fa]));//DP转移
}
inline void dfs(LL x,LL lst)//DFS遍历每一个节点
{
register LL i;
for(i=1;i<=m;++i) Down[x][i]=(Up[x][i]=sum[x])-a[lst];//初始化
for(i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst) dfs(e[i].to,x),DP(x,e[i].to,lst);//对每一个子节点进行DP
for(i=1;i<=m;++i) Down[x][i]=(Up[x][i]=sum[x])-a[lst];//一次DP可能有问题,因此要倒着再DP一遍,所以要重新初始化
for(i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst) Stack[++top]=e[i].to;//将每个节点加入一个栈中
while(top) dp(x,Stack[top--],lst);//倒着再DP一遍
ans=max(ans,max(Up[x][m],Down[x][m]));//更新ans
}
int main()
{
register LL i,x,y;
for(fread(ff,1,10000000,stdin),read(n),read(m),i=1;i<=n;++i) read(a[i]);
for(i=1;i<n;++i) read(x),read(y),add(x,y),add(y,x);
return dfs(1,0),write(ans),0;//这样只要以1号节点为根即可,然后输出ans
}

【洛谷4657】[CEOI2017] Chase(一个玄学的树形DP)的更多相关文章

  1. 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

    洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...

  2. 洛谷 P2279 [HNOI2003]消防局的设立 (树形dp or 贪心)

    一看到这道题就知道是树形dp 之前做过类似的题,只不过保护的范围是1 所以简单很多. 这道题保护的范围是2,就复杂了很多. 我就开始列状态,然后发现竟然有5种 然后我就开始列方程. 但是我考虑的时候是 ...

  3. 洛谷 P1352 没有上司的舞会【树形DP】(经典)

    <题目链接> <转载于>>> > 题目描述: 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的 ...

  4. 洛谷 P1352 没有上司的舞会【树形DP/邻接链表+链式前向星】

    题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...

  5. 洛谷 P2515 [HAOI2010]软件安装(缩点+树形dp)

    题面 luogu 题解 缩点+树形dp 依赖关系可以看作有向边 因为有环,先缩点 缩点后,有可能图不联通. 我们可以新建一个结点连接每个联通块. 然后就是树形dp了 Code #include< ...

  6. 【洛谷4815】[CCO2014] 狼人游戏(树形DP)

    点此看题面 大致题意: 已知有平民和狼人共\(n\)个,每个平民会指控和保护任何人,每个狼人只会指控平民.保护狼人.告诉你\(m\)对指控与保护的关系,求有\(k\)个狼人的方案总数. 树形\(DP\ ...

  7. 洛谷P3237 [HNOI2014]米特运输(树形dp)

    解题报告 题干 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都. ...

  8. 2018.07.22 洛谷P2986 伟大的奶牛聚集(树形dp)

    传送门 给出一棵树,树有边权和点权,若选定一个点作为中心,这棵树的代价是所有点权乘上到根的距离的和.求代价最小. 解法:一道明显的换根dp" role="presentation& ...

  9. 【洛谷】2607: [ZJOI2008]骑士【树形DP】【基环树】

    P2607 [ZJOI2008]骑士 题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的Y国发动了一 ...

随机推荐

  1. python 基于 wordcloud + jieba + matplotlib 生成词云

    词云 词云是啥?词云突出一个数据可视化,酷炫.以前以为很复杂,不想python已经有成熟的工具来做词云.而我们要做的就是准备关键词数据,挑一款字体,挑一张模板图片,非常非常无脑.准备好了吗,快跟我一起 ...

  2. Docker 容器的数据卷

    数据卷的特点: 1. 数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,这些数据会拷贝到新初始化的数据卷中 2. 数据卷可以在容器之间共享和重用 3. 可以对数据卷里的内容直接进行修改 ...

  3. DHCP DHCPv6

    为了给网络客户机自动分配IP地址以及生成所需的配置参数,IETF分别给IPV4和IPV6网络定义了相关的协议标准,即DHCP(RFC2131)和DHCPV6(RFC3315),以及扩充的选项标准.本文 ...

  4. JOS lab1 part2 分析

    lab1的Exercise 2就是让我们熟悉gdb的si操作,并知道BIOS的几条指令在做什么就够了,所以我们也会尽可能的去分析每一行代码. 首先进入到6.8282/lab这个目录下,输入指令make ...

  5. springMVC form表单提交多个对象集合--使用ajax提交--前台json格式数据封装方法

    (function ($) { $.fn.serializeJson = function () { var jsonData1 = {}; var serializeArray = this.ser ...

  6. SpringMVC(二)高级应用

    一.参数绑定-----集合类型 二.数据回显(例如提交表单失败了,数据没有丢失) 三.上传图片 四.json数据的交互 五.restful 支持 六.拦截器

  7. UVA 5986 - Wizarding Duel 超级脑洞题

    给出n个人,每个人两两比赛一场,一共有C(n,2)场比赛,现在给出一个榜,问其是否合法.不合法的话,就改成合法,输出最小需要改的变化. 分数一定是C(n,2)的了, 不和法的情况,比如0,0,2,是不 ...

  8. 牛客网Java刷题知识点之基本类型的自动转换和基本类型的强制转换

    不多说,直接上干货! TypeConvertDemo.java //自动类型转换 class TypeConvertDemo { public static void main(String[] ar ...

  9. Storm概念学习系列之Spout数据源

    不多说,直接上干货! Spout 数据源 消息源Spout是Storm的Topology中的消息生产者(即Tuple的创造者). Spout 介绍 1. Spout 的结构 Spout 是 Storm ...

  10. 新手写AIDL构建失败:...aidl.exe'' finished with non-zero exit value 1

    最近学习aidl,写demo后编译报错,跟着<Android开发艺术探索>以及网上的一些aidl详解博客敲完后一直编译不过,错误日志如下: Process 'command 'C:\Use ...