题目描述

https://www.luogu.org/problemnew/show/P3780

题解

一道思路巧妙的背包题。

对于那个奇怪的限制,我们对此稍加分析就可以发现它最后选择的区域是一个包含根节点的联通块。

对于\(t-h\leq k\)这个限制,我们可以把它看做是可以选择一条从根到某个节点的一条链,在这条链上不耗费任何代价的拿一个苹果,但是再去拿其它苹果是要有代价的。

根据贪心,我们的链一定是一直选到叶子的,所以我们最后是要枚举岁有的叶子。

在这个时候我们可以想到在枚举这个叶子的时候,这个叶子把dfs序分成了两半,我们可以预处理出前后缀的背包值,在枚举叶子的时候做一次背包合并。

然后我们的问题就变成了处理前后缀的背包值。

后面就比较神仙了,对于正着的部分,我们按照dfs先序遍历依次dp,进入这个节点的时候就把有代价的部分做背包,遍历儿子的时候就往下赋值,往上更新的时候把必须拿的那个更新上。

这样可以保证遍历到某个叶子的时候,上面的节点还没有动那个免费的苹果。

对于反着的部分,我们只有在往上更新的时候把免费的和付费的全部更新,这样可以保证到叶子的时候上面什么都没算,就可以放心合并了。

实现起来细节比较多%了\(Claris\)的代码。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#define N 20009
#define M 500009
using namespace std;
typedef long long ll;
const int maxnum=25600009;
int tot,head[N],a[N],b[N],dfn1[N],dfn2[N],n,k,siz,fa[N],K;
int fz[M],q[M],ans;
int dp1[maxnum],dp2[maxnum];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
struct edge{int n,to;}e[N];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void solve(int *dp,int a,int b){
int nowval=0;
int h=1,t=0;
for(int i=0;i<=k;++i){
dp[i]-=nowval;
while(h<=t&&dp[q[t]]<=dp[i])t--;
q[++t]=i;
while(h<=t&&q[h]+a<i)h++;
fz[i]=dp[q[h]]+nowval;
nowval+=b;
}
memcpy(dp,fz,siz);
}
void dfs1(int u){
int *now=dp1+dfn1[u]*K,nowa=a[u]-1,nowb=b[u];
if(nowa)solve(now,nowa,nowb);
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
dfn1[v]=++dfn1[0];
memcpy(dp1+dfn1[v]*K,now,siz);
dfs1(v);
}
if(fa[u]){
int *now1=dp1+dfn1[fa[u]]*K+1,*now2=dp1+dfn1[u]*K;
for(int j=1;j<=k;++j,now1++,now2++)*now1=max(*now1,*now2+b[u]);
}
}
void dfs2(int u,int val){
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
dfn2[v]=++dfn2[0];
memcpy(dp2+dfn2[v]*K,dp2+dfn2[u]*K,siz);
dfs2(v,val+b[v]);
}
if(!head[u]){
int *now1=dp1+dfn1[u]*K,*now2=dp2+dfn2[u]*K;
for(int j=0;j<=k;++j)ans=max(ans,*(now1+j)+*(now2+k-j)+val);;
}
int *now1=dp2+dfn2[u]*K,nowa=a[u]-1,nowb=b[u];
if(nowa)solve(now1,nowa,nowb);
if(fa[u]){
int *now1=dp2+dfn2[u]*K;
int *now2=dp2+dfn2[fa[u]]*K;now2++;
for(int j=1;j<=k;++j,now1++,now2++)*now2=max(*now2,*now1+b[u]);///!!!!!!!!!!
}
}
inline void unit(){
memset(dp1,0,sizeof(dp1));
memset(dp2,0,sizeof(dp2));
memset(head,0,sizeof(head));
memset(dfn1,0,sizeof(dfn1));
memset(dfn2,0,sizeof(dfn2));
tot=ans=0;
}
int main(){
int T=rd();
while(T--){
n=rd();k=rd();K=k+1;
siz=K*sizeof(int);
unit();
for(int i=1;i<=n;++i){
fa[i]=rd();
if(fa[i])add(fa[i],i);
a[i]=rd();b[i]=rd();
}
dfs1(1);
memset(head,0,sizeof(head));
tot=0;
for(int i=n;i>=1;--i)if(fa[i])add(fa[i],i);
dfs2(1,b[1]);
printf("%d\n",ans);
}
return 0;
}

[SDOI2017]苹果树的更多相关文章

  1. sdoi2017苹果树

    题解: 非常奇妙的一题.. 没有免费操作我都不会$nk$....考试打个暴力就可以走人了 树上有依赖背包问题的正确做法是(为啥我之前学的不是这样的啊) 按照后续遍历做背包 做到一个点的时候 枚举它选不 ...

  2. BZOJ.4910.[SDOI2017]苹果树(树形依赖背包 DP 单调队列)

    BZOJ 洛谷 \(shadowice\)已经把他的思路说的很清楚了,可以先看一下会更好理解? 这篇主要是对\(Claris\)题解的简单说明.与\(shadowice\)的做法还是有差异的(比如并没 ...

  3. BZOJ4910 : [Sdoi2017] 苹果树

    问题等价于树形依赖背包,允许一条链每个点各免费一次. 设$f[i][j]$表示按DFS序考虑到$i$,体积为$j$的最大收益. 先放入不能免费的物品,等遍历完儿子后再放入必选的物品,那么$i$到根路径 ...

  4. 【做题】SDOI2017苹果树——dfs序的运用

    原文链接 https://www.cnblogs.com/cly-none/p/9845046.html 题意:给出一棵\(n\)个结点的树,在第\(i\)个结点上有\(a_i\)个权值为\(v_i\ ...

  5. hs-black 杂题选讲

    [POI2011]OKR-Periodicity 考虑递归地构造,设 \(\text{solve(s)}\) 表示字典序最小的,\(\text{border}\) 集合和 \(S\) 的 \(\tex ...

  6. DP 优化小技巧

    收录一些比较冷门的 DP 优化方法. 1. 树上依赖性背包 树上依赖性背包形如在树上选出若干个物品做背包问题,满足这些物品连通.由于 01 背包,多重背包和完全背包均可以在 \(\mathcal{O} ...

  7. 【LOJ】#2268. 「SDOI2017」苹果树

    题解 显然权值都是正的,我们最深的那个点一定延伸到了某个叶子 我们抛去这条链之外再选K个点即可 如果直接对一棵树选K个点,满足这样的依赖关系,可以通过一个后序遍历的顺序做出来 转移方法是 \(dp[i ...

  8. SDOI2017 Round2 详细题解

    这套题实在是太神仙了..做了我好久...好多题都是去搜题解才会的 TAT. 剩的那道题先咕着,如果省选没有退役就来填吧. 「SDOI2017」龙与地下城 题意 丢 \(Y\) 次骰子,骰子有 \(X\ ...

  9. codevs 1228 苹果树 树链剖分讲解

    题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上 ...

随机推荐

  1. PHP的简单跳转提示的实现

    在PHP开发中,尤其是MVC框架或者项目中,会碰到很多跳转情况,比如:登录成功或失败后的跳转等等. 以下以MVC框架开发中为基础,示例讲解: 在基础控制器类中:Conrtoller.class.php ...

  2. 头部banner根据网址高亮

    $(function(){ var urlstr = location.href; $(".nav li a").each(function () { if ((urlstr + ...

  3. WebGL或OpenGL关于模型视图投影变换的设置技巧

    目录 1. 具体实例 2. 解决方案 1) Cube.html 2) Cube.js 3) 运行结果 3. 详细讲解 1) 模型变换 2) 视图变换 3) 投影变换 4) 模型视图投影矩阵 4. 存在 ...

  4. Java-获取年月日对应的天干地支

    一.概述 本次是以java语言开发为例,计算出年月日对应的天干地支. 二.代码 public class MyDate { /** * 对于年月日的天干地支 */ private int year_g ...

  5. 安卓9.0系统机器(亲测有效)激活Xposed框架的步骤

    对于喜欢玩手机的哥们来说,经常会用到xposed框架及其种类繁多功能无敌的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活xposed框架是非常简便的,但随着系统版本的持续更新, ...

  6. SpringMVC归纳-1(model数据模型与重定向传参技术)

    要点: model是一个Map结构的数据模型,能重定向时传递数据(拼接URL),但不安全,主要用于渲染前端页面,配合Thymeleaf填充html里面里设置好的参数. @RequestParam用来获 ...

  7. 深入浅出KNN算法(一) KNN算法原理

    一.KNN算法概述 KNN可以说是最简单的分类算法之一,同时,它也是最常用的分类算法之一,注意KNN算法是有监督学习中的分类算法,它看起来和另一个机器学习算法Kmeans有点像(Kmeans是无监督学 ...

  8. SQL常用语句(二)

    --语 句 功 能--数据操作SELECT --从数据库表中检索数据行和列INSERT --向数据库表添加新数据行DELETE --从数据库表中删除数据行UPDATE --更新数据库表中的数据 --数 ...

  9. 猴子选大王 (约瑟夫环)(c#)

    猴子选大王问题: 一堆猴子都有编号,编号是1,2,3 ...m ,这群猴子(m个)按照1到m的顺序围坐一圈, 从第1开始数,每数到第n个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子, ...

  10. gradle 编译war包出现乱码,设置为utf-8格式

    1.找gradle 安装目录下的 gradle 2.修改 DEFAULT_JVM_OPTS="-Dfile.encoding=UTF-8"