首先我们知道这棵树的形态,一眼DP。
考虑蓝线的性质,显然蓝线在树上是连接连续三个节点的。
这样就有三种情况:连接 一个节点 的 某个孩子->本身->父亲 或者 一个孩子->本身->另一个孩子。
然后胡乱写一个DP,发现并不能过大样例。
因为这种想法定义是有反例的!
考虑下面的这棵树的连边方式,怎样生成连边方式是这样的一棵树?


显然是不可能做到的,因为下面的两个叶子中需要有两个根才行,然而我们只能有一个根。
也就是说,确定了根以后,我们的连边方式就只剩下了 一个节点 的 某个孩子->本身->父亲 这一种。
(显然只有使用且仅使用这一种方式能生成可行的树)。
我们令f[i][0/1]表示考虑节点i及节点i的子树,i是否为蓝边的中点,能获得的最大价值。
显然我们有:
f[i][0] = sigma( j : son[i] ) max( f[j][0] , f[j][1] + edge[i->j] ) ,
f[i][1] = sigma( j : son[i] ) max( f[i][0] - max( f[j][0] , f[j][1] + edge[i->j] ) + f[j][0] + edge[i->j] )。
这样我们就能枚举根然后O(n^2)暴力DP了。
仔细观察这个转移方程,显然我们可以换根。
我们设g[i][j][0/1]表示节点i不考虑孩子j的f[i][0/1]。
我们有:
g[i][j][0] = sigma( t : son[i] && t != j ) max( f[t][0] , f[t][1] + edge[i->t] ) ,
g[i][j][1] = sigma( t : son[i] && t != j ) max( g[i][j][0] - max( f[t][0] , f[t][1] + edge[i->t] ) + f[t][0] + edge[i->t] )。
显然有:
g[i][j][0] = f[i][0] -  max( f[j][0] , f[j][1] + edge[i->j] )。
对于g[i][j][1],我们可以把后面的那一堆东西提出来,那是一个仅与变量t相关的表达式。我们维护这个表达式的前缀max和后缀max就好了。
换根的话,我们就把某个节点的父亲的g[i][j][0/1]当做j的一个孩子去处理就行了。

代码:

 #include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=2e5+1e2;
const int inf=0x3f3f3f3f; int s[maxn],t[maxn<<],nxt[maxn<<],l[maxn<<];
vector<int> son[maxn],pef[maxn],suf[maxn],len[maxn];
int f[maxn][],ans; inline void addedge(int from,int to,int len) {
static int cnt = ;
t[++cnt] = to , l[cnt] = len ,
nxt[cnt] = s[from] , s[from] = cnt;
}
inline void dfs1(int pos,int fa) {
f[pos][] = , f[pos][] = -inf;
for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa ) son[pos].push_back(t[at]) , len[pos].push_back(l[at]);
for(unsigned i=;i<son[pos].size();i++) {
dfs1(son[pos][i],pos) , f[pos][] += max( f[son[pos][i]][] , f[son[pos][i]][] + len[pos][i] );
pef[pos].push_back(f[son[pos][i]][]-max(f[son[pos][i]][],f[son[pos][i]][]+len[pos][i])+len[pos][i]) ,
suf[pos].push_back(f[son[pos][i]][]-max(f[son[pos][i]][],f[son[pos][i]][]+len[pos][i])+len[pos][i]) ;
}
for(unsigned i=;i<son[pos].size();i++) f[pos][] = max( f[pos][] , f[pos][] + pef[pos][i] );
for(unsigned i=;i<son[pos].size();i++) pef[pos][i] = max( pef[pos][i] , pef[pos][i-] );
for(int i=(signed)son[pos].size()-;i>=;i--) suf[pos][i] = max( suf[pos][i] , suf[pos][i+] );
}
inline void dfs2(int pos,int ffa0,int ffa1,int lfa) {
f[pos][] += max( ffa0 , ffa1 + lfa ) , f[pos][] += max( ffa0 , ffa1 + lfa ) , f[pos][] = max( f[pos][] , f[pos][] + ffa0 - max( ffa0 , ffa1 + lfa ) + lfa );
ans = max( ans , f[pos][] );
for(unsigned i=;i<son[pos].size();i++) {
int fnow0 = f[pos][] - max( f[son[pos][i]][] , f[son[pos][i]][] + len[pos][i] );
int delta = ffa0 - max( ffa0 , ffa1 + lfa ) + lfa;
if( i != ) delta = max( delta , pef[pos][i-] );
if( i != son[pos].size() - ) delta = max( delta , suf[pos][i+] );
dfs2(son[pos][i],fnow0,fnow0+delta,len[pos][i]);
}
} int main() {
static int n;
scanf("%d",&n);
for(int i=,a,b,l;i<n;i++) scanf("%d%d%d",&a,&b,&l) , addedge(a,b,l) , addedge(b,a,l);
dfs1(,-) , dfs2(,,-inf,-inf) , printf("%d\n",ans);
return ;
}

(换回了U2417H的我压行更加肆无忌惮了)

Bzoj3677:树形DP的更多相关文章

  1. [Bzoj3677][Apio2014]连珠线(树形dp)

    3677: [Apio2014]连珠线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 434  Solved: 270[Submit][Status] ...

  2. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  3. COGS 2532. [HZOI 2016]树之美 树形dp

    可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...

  4. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

  5. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)

    题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...

  6. 树形DP

    切题ing!!!!! HDU  2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...

  7. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  8. POJ2342 树形dp

    原题:http://poj.org/problem?id=2342 树形dp入门题. 我们让dp[i][0]表示第i个人不去,dp[i][1]表示第i个人去 ,根据题意我们可以很容易的得到如下递推公式 ...

  9. hdu1561 The more, The Better (树形dp+背包)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1561 思路:树形dp+01背包 //看注释可以懂 用vector建树更简单. 代码: #i ...

随机推荐

  1. Linux-Xshell会话保持

    1.Xshell客户端设置 2.服务端设置 修改/etc/ssh/sshd_config文件,找到 ClientAliveInterval 0和ClientAliveCountMax 3并将注释符号( ...

  2. SQL语句(十三)多表查询

    多表查询 1. 笛卡尔乘积 简单格式 SELECT * 或字段列表 FROM 数据表列表 实例 --1. 笛卡尔乘积 (五条件的连接--很多条件无意义) Select * from Student, ...

  3. JavaScript 删除 ASP.NET 设置的多值 Cookie 的方法

    需要注意HttpOnly,Path等属性.完整的测试代码: ASPX 代码<%@ Page Language="C#" %> <!DOCTYPE html PUB ...

  4. AngularJS入门基础——作用域

    作用域$scope是构成AngularJS应用的核心基础,在整个框架中都被广泛使用,因此了解它是非常重要的. $scope对像是定义应用业务逻辑,控制器方法和视图属性的地方.作用域是视图和控制器之间的 ...

  5. HTML5 移动开发(CSS3设计移动页面样式)

    1.如何创建CSS样式表 2.CSS3的卓越特性 3.基于设备属性改变样式的媒体查询 4.如何使用属性改变元标签创建更美观移动页面   层叠样式表是移动WEB开发中的一个重要组成部分,本次分享将学到如 ...

  6. ios TextField限制输入两位小数

    只需要实现textField的这个代理方法就可以实现 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange: ...

  7. TensorFlow 从零到helloWorld

    目录 1.git安装与使用 1.1 git安装 1.2 修改git bash默认路径 1.3 git常用操作 2.环境搭建   2.1 tensorflow安装   2.2 CUDA安装   2.3 ...

  8. Redis常见操作命令

    1.库相关 select 索引 => 选择库 dbsize => 查询当前库中Key的数量 flushdb => 清空当前库 flushall => 清空所有库(建议不要用,除 ...

  9. C#使用redis学习笔记

    1.官网:http://redis.io/(英)  http://www.redis.cn/(中) 2.下载:https://github.com/dmajkic/redis/downloads(Wi ...

  10. hdu 4348 To the moon (主席树)

    版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 4348 题意: 一个长度为n的数组,4种操作 : (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 . (2)Q ...