【LG3647】[APIO2014]连珠线
【LG3647】[APIO2014]连珠线
题面
题解
首先考虑一下蓝线连起来的情况,一定是儿子-父亲-另一个儿子或者是儿子-父亲-父亲的父亲。
而因为一开始只有一个点在当前局面上,将一条红边变为两条蓝边也只能在一对有父子关系的点之间加,所以第一种“儿子-父亲-另一个儿子”的情况实际上是不存在的。
假设我们当前已经选定根节点,设\(f[i][0/1]\)表示当前位于以\(i\)为根的子树\(i\)不作为/作为中转点(即中间的父亲节点)的最大答案。
那么有转移:
\(f[i][0]=\sum_{j\in son_i} max(f[j][0],f[j][1]+cost_{i,j})\)
钦定一个儿子连上来,得到另一部分的转移:
\(f[i][1]=max_{j\in son_i}\{f[i][0]-max(f[j][0],f[j][1]+cost_{i,j})+f[j][1]+cost_{i,j}\}\)
因为根在哪个位置会使我们的情况受到影响,考虑换根。
令\(g[i][j][0/1]\)表示当前\(i\)不考虑\(j\)这个儿子和作不作为中转点的最大答案。
那么\(g[i][j][0]\)显然为\(f[i][0]-max(f[j][0],f[j][1]+cost_{i,j})\)。
\(g[i][j][1]\)稍微麻烦一些,需要记录转移上来的\(max(f[j][0],f[j][1]+cost_{i,j})+f[j][1]+cost_{i,j}\)最大及次大值,对于从最大值转移上来的儿子,即为次大值的贡献,否则为最大值的贡献。
然后因为这个\(g\)不能直接开数组,所以用\(vector\)代替。
有这个东西就可以偷税的换根了,换根过程详见代码。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int INF = 2e9;
const int MAX_N = 2e5 + 5;
struct Graph { int to, cost, next; } e[MAX_N << 1];
int fir[MAX_N], e_cnt;
void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; }
void Add_Edge(int u, int v, int w) { e[e_cnt] = (Graph){v, w, fir[u]}, fir[u] = e_cnt++; }
int N, fa[MAX_N];
int f[MAX_N][2], cost[MAX_N];
vector<int> g[MAX_N][3], son[MAX_N], mx[MAX_N];
void dfs(int x) {
f[x][0] = 0, f[x][1] = -INF;
for (int i = fir[x]; ~i; i = e[i].next)
if (e[i].to != fa[x]) fa[e[i].to] = x, dfs(e[i].to);
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to; if (v == fa[x]) continue;
son[x].push_back(v), cost[v] = e[i].cost;
f[x][0] += max(f[v][0], f[v][1] + e[i].cost);
}
int m1 = -INF, m2 = -INF;
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to; if (v == fa[x]) continue;
int val = f[v][0] + e[i].cost - max(f[v][0], f[v][1] + e[i].cost);
f[x][1] = max(f[x][1], f[x][0] + val);
if (val > m1) m2 = m1, m1 = val;
else if (val > m2) m2 = val;
}
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to; if (v == fa[x]) continue;
g[x][0].push_back(f[x][0] - max(f[v][0], f[v][1] + e[i].cost));
int val = f[v][0] + e[i].cost - max(f[v][0], f[v][1] + e[i].cost);
if (val == m1) g[x][1].push_back(g[x][0].back() + m2), mx[x].push_back(m2);
else g[x][1].push_back(g[x][0].back() + m1), mx[x].push_back(m1);
}
}
int ans = 0;
void rdfs(int x) {
for (int i = 0; i < (int)son[x].size(); i++) {
f[x][0] = g[x][0][i], f[x][1] = g[x][1][i];
if (fa[x]) {
f[x][0] += max(f[fa[x]][0], f[fa[x]][1] + cost[x]);
f[x][1] = f[x][0] + max(mx[x][i], f[fa[x]][0] + cost[x] - max(f[fa[x]][0], f[fa[x]][1] + cost[x]));
}
ans = max(ans, f[son[x][i]][0] + max(f[x][0], f[x][1] + cost[son[x][i]]));
rdfs(son[x][i]);
}
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
clearGraph();
N = gi();
for (int i = 1; i < N; i++) {
int u = gi(), v = gi(), w = gi();
Add_Edge(u, v, w);
Add_Edge(v, u, w);
}
dfs(1);
ans = f[1][0];
rdfs(1);
printf("%d\n", ans);
return 0;
}
【LG3647】[APIO2014]连珠线的更多相关文章
- 【BZOJ3677】[Apio2014]连珠线 换根DP
[BZOJ3677][Apio2014]连珠线 Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色 ...
- [Bzoj3677][Apio2014]连珠线(树形dp)
3677: [Apio2014]连珠线 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 434 Solved: 270[Submit][Status] ...
- 题解 [APIO2014]连珠线
题解 [APIO2014]连珠线 题面 解析 首先这连成的是一棵树啊. 并且\(yy\)一下,如果钦定一个根, 那么这上面的蓝线都是爸爸->儿子->孙子这样的,因为像下图这样的构造不出来: ...
- bzoj3677: [Apio2014]连珠线
Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的 ...
- APIO2014 连珠线
题目链接:戳我 换根DP 由于蒟蒻不会做这个题,所以参考了大佬. 本来想的是有三种情况,一种是该节点不作为两个蓝线的中点(我们称这种不是关键节点),一种是该节点作为关键点.连两个子节点,一种是作为关键 ...
- bzoj 3677: [Apio2014]连珠线【树形dp】
参考:http://www.cnblogs.com/mmlz/p/4456547.html 枚举根,然后做树形dp,设f[i][1]为i是蓝线中点(蓝线一定是父子孙三代),f[i][0]为不是,转移很 ...
- Luogu P3647 [APIO2014]连珠线
题目 换根dp. 显然对于给定的一棵有根树,蓝线都不能拐弯. 设\(f_{u,0}\)表示\(u\)不是蓝线中点时子树内的答案,\(f_{u,1}\)表示\(u\)是蓝线中点时子树内的答案.(以\(1 ...
- 洛谷$P3647\ [APIO2014]$连珠线 换根$dp$
正解:换根$dp$ 解题报告: 传送门! 谁能想到$9102$年了$gql$居然还没写过换根$dp$呢,,,$/kel$ 考虑固定了从哪个点开始之后,以这个点作为根,蓝线只可能是直上直下的,形如&qu ...
- 洛谷 P3647 [APIO2014]连珠线(换根 dp)
题面传送门 题意: 桌子上有 \(1\) 个珠子,你要进行 \(n-1\) 次操作,每次操作有以下两种类型: 拿出一个新珠子,并选择一个桌子上的珠子,在它们之间连一条红线 选择两个由红线相连的珠子 \ ...
随机推荐
- 新的部署架构之下,如何拿shell?
和朋友聊起一个话题,服务器部署架构升级对安全的影响.从最简单的一台服务器,到应用.数据库.文件服务器分离:从本地机房服务器到云服务器产品矩阵:从虚拟化到容器化部署,一直在往更安全的方向改变. 本文试图 ...
- nginx 配置反向代理,负载均衡实战解析
前言:NGINX的反向代理和负载均衡是网站架构中经常用到的一种高并发,高可用的方案,下面我们直接实战操作,当然理论也是要的. 一.反向代理 过程:反向代理:客户端 一>代理 <一> ...
- .net core 2.2部署到Windows Server 2012 R2 standard
安装.net core 2.2 下载地址:https://dotnet.microsoft.com/download/dotnet-core/2.2 找到ASP.NET Core Runtime 2. ...
- gentelella 开源后台使用记录
前言 gentelella是一款开源后台,github地址是:https://github.com/ColorlibHQ/gentelella 使用 表单验证 parsley 验证 在form.htm ...
- winfrom判断程序是否运行,并给提示
在Program.cs文件中修改为: private static System.Threading.Mutex mutex; /// <summary> /// 应用程序的主入口点. / ...
- 常用的python内置模块
1.time模块: time模块是普通的时间模块 在python的三种时间表现形式: 1.时间戳: 给电脑看的. - 自1970-01-01 00:00:00到当前时间,按秒计算,计算了多少秒. 2. ...
- Centos下安装LoadRunner负载机
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAd0AAADfCAIAAAA84J6GAAAgAElEQVR4nJzb93cbR6In+uK/8v6Fd8
- express和express-generator搭建web应用
nodemon的安装使用 安装 npm i nodemon -D 配置 新建nodemon.json { "watch": "src/**/*.* } 修改package ...
- shell脚本初学者笔记
概述 Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务. Shell 脚本(shell script),是一种为 shell 编写的脚本程序. Linu ...
- shell公共函数functions
checkpid:检查是否已存在pid,如果有一个存在,返回0(通过查看/proc目录) daemon:启动服务 killproc:杀死某个进程 pidfileofproc:寻找某个进程的pid pi ...