题意:

有一棵棵提米树,满足这样的性质:

每个点上长了一定数量的Temmie 薄片,薄片数量记为这个点的权值,这些点被标记为 1 到 n 的整数,其

中 1 号点是树的根,没有孩子的点是树上的叶子。

定义\((a,b)\)是一对相邻的叶子,当且仅当没有其它的叶子节点在 DFS 序上在a,b 之间。

每对相邻的叶子都会产生一个代价,代价为 a 到 b 路径上(不包含 a,b)的点中,最大点权值。

提米树可以提供决心,一棵提米树能提供的决心的数量是树上所有叶子上长的 Temmie 薄片数量和,减去所有相邻叶子的代价。

Temmie 们决定对这棵树进行若干次剪枝(可以不剪枝),使得这棵树能提供的决心最多。

一次剪枝定义为:如果一个点的孩子都是叶子,就可以把它所有的孩子剪掉。

要求\(O(n)\)做法。

首先,考虑\(O(n^2)\)的60分暴力:

我们可以反过来:由根开始,每个节点考虑是否扩展出所有叶子。

若不扩展,则它的子树都是空的,它成为叶子。

我们可以在dfs序上DP:设\(dp(i,j)\)表示考虑到i,上一个叶子点为j的最大决心。

有两种转移:

1、若i不是叶子,可以扩展,转移到\(dp(i+1,j)\)。

2、可以不扩展,转移到\(dp(i+si_i,i)+w_i-max(i,j)\)。

代码如下:

for(int i=tm-1;i>=0;i--)
{
for(int j=0;j<=n;j++)
{
int u=xl[i];
dp[i][j]=dp[i+si[u]][u]+sz[u]-zd[j][u];
if(si[u]>1&&dp[i+1][j]>dp[i][j])
dp[i][j]=dp[i+1][j];
}
}
printf("%d",dp[0][0]);

考虑优化:首先,要把维度降下来。

设\(dp(i)\)表示i成为叶子后的最大决心。

枚举下一个使用2转移的位置,代码如下:

for(int i=tm;i>=0;i--)
{
int u=xl[i];
dp[i]=-999999999;
for(int j=i+si[u];j<=tm+1;j++)
{
int t=dp[j]-zd[u][xl[j]];
if(t>=dp[i])
dp[i]=t;
if(si[xl[j]]==1)//注意此处,非常关键。
break;
}
dp[i]+=sz[u];
}
printf("%d",dp[0]);

继续优化:

我们发现,对于\(i\),\(i+si_i\)就是i的祖先节点中第一个有更右子节点的点。

而由于注释处的break,使得转移就是在\(i+si[u]\)处,一直向左走形成的链。

那么:

绿点对橙点有贡献。

那么,我们枚举红点lca,再枚举相邻的两个儿子,计算贡献。

先算出链上每个节点到lca的最大值。设为\(h\),那么,就是\(dp(u)=max(dp(v)-max(h(u),h(v)))+w(u)\)。

由于h具有单调性,因此分\(h(u)>h(v)\)和\(h(u)<=h(v)\)进行讨论,提前算出链上\(dp\),以及\(dp-h\)的最大值。

这两种情况符合的v一定是前缀/后缀,双指针扫一下即可定位。

代码细节非常多。

#include <stdio.h>
#include <vector>
#define inf 999999999
using namespace std;
vector<int> ve[100010];
int sz[100010],cl[100010],cr[100010],dp[100010],zd[100010],fa[100010],ma[100010],md[100010];
int max(int a,int b)
{
return a>b?a:b;
}
void dfs0(int u,int f)
{
fa[u]=f;
for(int i=0;i<ve[u].size();i++)
dfs0(ve[u][i],u);
}
void dfs1(int u)
{
for(int i=ve[u].size()-2;i>=0;i--)
{
dfs1(ve[u][i+1]);
int t=ve[u][i],la=0;zd[u]=sz[u];
while(t!=0)
{
zd[t]=max(sz[t],zd[fa[t]]);
t=cr[t];
}
t=ve[u][i+1];
while(t!=0)
{
zd[t]=max(sz[t],zd[fa[t]]);
la=t;t=cl[t];
}
t=la;while(t!=u)
{
ma[t]=dp[t]-zd[fa[t]];
if(cl[t])ma[t]=max(ma[t],ma[cl[t]]);
t=fa[t];
}
t=ve[u][i+1];
while(t!=0)
{
md[t]=dp[t];
if(fa[t]!=u)md[t]=max(md[t],md[fa[t]]);
t=cl[t];
}
ma[u]=md[u]=-inf;
int x=ve[u][i],y=ve[u][i+1];
while(x!=0)
{
while(y!=0&&zd[fa[y]]<zd[fa[x]])
y=cl[y];
if(y)
{
t=ma[y]+sz[x];
if(t>dp[x])dp[x]=t;
}
t=md[(y==0?la:fa[y])]-zd[fa[x]]+sz[x];
if(t>dp[x])dp[x]=t;x=cr[x];
}
}
if(ve[u].size())dfs1(cl[u]);
}
int main()
{
freopen("temmie.in","r",stdin);
freopen("temmie.out","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int s,a;
scanf("%d%d",&sz[i],&s);
for(int j=0;j<s;j++)
{
scanf("%d",&a);
ve[i].push_back(a);
}
if(s>0)
{
cl[i]=ve[i][0];
cr[i]=ve[i][s-1];
}
dp[i]=-inf;
}
int u=1;
while(u!=0)
{
dp[u]=sz[u];
u=cr[u];
}
dfs0(1,0);dfs1(1);
int ma=-inf;u=1;
while(u!=0)
{
if(dp[u]>ma)
ma=dp[u];
u=cl[u];
}
printf("%d",ma);
return 0;
}

模拟赛 提米树 题解 (DP+思维)的更多相关文章

  1. [NOIP10.6模拟赛]2.equation题解--DFS序+线段树

    题目链接: 咕 闲扯: 终于在集训中敲出正解(虽然与正解不完全相同),开心QAQ 首先比较巧,这题是\(Ebola\)出的一场模拟赛的一道题的树上强化版,当时还口胡出了那题的题解 然而考场上只得了86 ...

  2. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

  3. [AGC011F] Train Service Planning [线段树优化dp+思维]

    思路 模意义 这题真tm有意思 我上下楼梯了半天做出来的qwq 首先,考虑到每K分钟有一辆车,那么可以把所有的操作都放到模$K$意义下进行 这时,我们只需要考虑两边的两辆车就好了. 定义一些称呼: 上 ...

  4. 模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)

    题面 题解 先解决第一个子问题吧,它才是难点 Subtask_1 我们可以先用一个简单的树形DP处理出每棵树内部的dis和,记为dp0[i], 然后再用一个换根的树形DP处理出每棵树内点 i 到树内每 ...

  5. [NOIP10.5模拟赛]3.c题解--思维

    题目链接 这次不咕了 https://www.luogu.org/problemnew/show/AT2389 闲扯 考场20分爆搜走人 \cy 话说这几天T3都很考验思维啊 分析 我们先钦定一只鸡( ...

  6. [NOIP10.3模拟赛]3.w题解--神奇树形DP

    题目链接: 咕 闲扯: 这题考场上把子任务都敲满了,5个namespace,400行11k 结果爆0了哈哈,因为写了个假快读只能读入一位数,所以手测数据都过了,交上去全TLE了 把边分成三类:0. 需 ...

  7. [NOIP10.4模拟赛]3.z题解--思维

    题目链接: 咕咕 闲扯: 哈哈这道T3考场上又敲了5个namespace,300+行,有了前车之鉴还对拍过,本以为子任务分稳了 结果只有30分哈哈,明明用极限数据对拍过不知怎么回事最后数据又是读不全, ...

  8. 省选模拟赛 4.26 T1 dp 线段树优化dp

    LINK:T1 算是一道中档题 考试的时候脑残了 不仅没写优化 连暴力都打挂了. 容易发现一个性质 那就是同一格子不会被两种以上的颜色染.(颜色就三种. 通过这个性质就可以进行dp了.先按照左端点排序 ...

  9. [NOIP10.6模拟赛]1.merchant题解--思维+二分

    题目链接: while(1)gugu(while(1)) 闲扯 考场上怕T2正解写挂其他两题没管只打了暴力,晚上发现这题思维挺妙的 同时想吐槽出题人似乎热衷卡常...我的巨大常数现在显露无疑QAQ 分 ...

随机推荐

  1. DjangoRestFramework学习二之序列化组件、视图组件

    本节目录 一 序列化组件 二 视图组件 三 xxx 四 xxx 五 xxx 六 xxx 七 xxx 八 xxx 一 序列化组件 首先按照restful规范咱们创建一些api接口,按照下面这些形式写吧: ...

  2. @Autowired 和 @Resource注解, 一个接口有多个实现类的时候Spring注入遇到的问题

    先说下我遇到的问题,有一个接口 CompensationService, 有两个实现类 MusicCompensationStrategyImpl  和  TakeDeliveryCompensati ...

  3. LOJ2882 JOISC2014 两个人的星座 计算几何

    传送门 一件值得注意的事情是:平面上两个不相交的三角形一定会存在两条公切线 那么我们可以枚举三角形的公切线,计算有多少个三角形的公切线之一为该线,所有的答案除以2就是我们要求的答案. 考虑如何去计算有 ...

  4. 全栈项目|小书架|服务器开发-NodeJS 使用 JWT 实现登录认证

    通过这篇 全栈项目|小书架|服务器开发-JWT 详解 文章我们对JWT有了深入的了解,那么接下来介绍JWT如何在项目中使用. 安装 $ npm install jsonwebtoken 生成 Toke ...

  5. HBuilder 方便局域网访问调试

    同一个局域网,通过IP不能访问我本地的项目,各种测试发现原来是防火墙的问题: 这里附上参考文档:内置web服务器被防火墙禁用导致预览和运行异常的解决方案

  6. spark源码阅读--SparkContext启动过程

    ##SparkContext启动过程 基于spark 2.1.0  scala 2.11.8 spark源码的体系结构实在是很庞大,从使用spark-submit脚本提交任务,到向yarn申请容器,启 ...

  7. 微服务与SpringCloud简介

    A.官网 https://spring.io/projects/spring-cloud B.简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用 ...

  8. c# zip写comment注释

    //生成的压缩文件为test.zip using (FileStream fsOut = File.Create("test.zip")) { //ZipOutputStream类 ...

  9. 关于近期使用webpack所引发的思考

    近期,(使其也挺长时间了).使用了一段时间的webpack. 但是在使用期间个人感觉并不想网上说的那样好,个人对webpack的评价并不是很高,甚至有点反感使用webpack. 个人感觉使用webpa ...

  10. mysql学习之基础篇07

    视图:view 在查询的时候我们经常把查询到的结果当成一张临时表来看,其实view就可以看成一张虚拟表,是表通过某种运算得到的投影 那么如何创建视图?创建视图需要指定视图的列名和列类型吗? 答:不用, ...