BZOJ 3677 连珠线
Description
在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为\(1\)到\(n\)。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:
\(Append(w, v)\):一个新的珠子\(w\)和一个已经添加的珠子\(v\)用红线连接起来。
\(Insert(w, u, v)\):一个新的珠子\(w\)插入到用红线连起来的两个珠子\(u,v\)之间。具体过程是删去\(u,v\)之间红线,分别用蓝线连接\(u,w\)和\(w,v\)。
每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。
给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。
你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。
Input
第一行是一个正整数\(n\),表示珠子的个数,珠子编号为\(1\)到\(n\)。
接下来\(n-1\)行,每行三个正整数\(a_{i},b_{i}(1 \le a_{i} \le 10000)\),表示有一条长度为\(c_{i}\)的线连接了珠子\(a_{i}\)和珠子\(b_{i}\)。
Output
输出一个整数,为游戏的最大得分。
Sample Input
5
1 2
1 3 4 0
1 4 1 5
1 5 2 0
Sample Output
60
HINT
数据范围满足\(1 \le n \le 200000\)。
这题我开始YY树形dpYY了很久,都被自己拍死了,后来是hmr大神(跪烂)告诉了结论的。
对于某种合法的红蓝线分布情况,一定满足蓝线分布在\(son_{i},i,father_{i}\)之间。
于是\(O(n^{2})\)dp就出来了。每次换根进行dp即可。状态\(f_{i,1}\)表示以\(i\)为根的子树,\(i\)是蓝线的终点所能得到的最大收益;相反\(f_{i,0}\)表示以\(i\)为根的子树,\(i\)不是蓝线的中点所能够得到的最大收益。
令\(cost_{i}\)表示\(i\)与\(father_{i}\)所连的边的权值大小,那么就有转移$$f_{i,0}=\sum_{j \in son_{i}}max(f_{j,0},f_{j,1}+cost_{j})$$
\]
正解就是只进行一遍dp,每次\(O(1)\)进行换根转移。
我们在第一次dp的时候,记录一下\(dp_{i,j,0}\)和\(dp_{i,j,1}\)的值,表示\(i\)不考虑\(j\)这个儿子的\(f_{i,0/1}\)(\(dp{i,j,1}\)的用\(-max(f_{j,0},f_{j,1}+cost_{j})+f_{j,0}+cost_{j})\;(j \in son_{i})\)的最大值和次大值维护即可)。
每次换根从父亲换到儿子,只会修改两个点的\(f\)值,将这两个点用\(dp\)再转移一遍即可。
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
using namespace std;
#define inf (1<<29)
#define maxn (500010)
int n,side[maxn],toit[maxn*2],next[maxn*2],len[maxn],f[maxn][2],cnt,ans,father[maxn],cost[maxn];
vector <int> son[maxn],dp[maxn][2],best[maxn];
inline void add(int a,int b,int c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; len[cnt] = c; }
inline void ins(int a,int b,int c) { add(a,b,c); add(b,a,c); }
inline void update(int &a,int b) { if (b < 0) return; if (b > a) a = b; }
inline void dfs(int now)
{
f[now][1] = -inf; f[now][0] = 0;
int fb = -inf,sb = -inf;
for (int i = side[now];i;i = next[i])
{
if (toit[i] == father[now]) continue;
cost[toit[i]] = len[i]; father[toit[i]] = now; dfs(toit[i]);
f[now][0] += max(f[toit[i]][0],f[toit[i]][1]+len[i]);
if (-max(f[toit[i]][0],f[toit[i]][1]+len[i])+f[toit[i]][0]+len[i]>fb)
sb = fb,fb = -max(f[toit[i]][0],f[toit[i]][1]+len[i])+f[toit[i]][0]+len[i];
else if (-max(f[toit[i]][0],f[toit[i]][1]+len[i])+f[toit[i]][0]+len[i] > sb)
sb = -max(f[toit[i]][0],f[toit[i]][1]+len[i])+f[toit[i]][0]+len[i];
}
f[now][1] = f[now][0] + fb;
for (int i = side[now],tmp,key;i;i = next[i])
{
if (toit[i] == father[now]) continue;
son[now].push_back(toit[i]);
dp[now][0].push_back(tmp = f[now][0] - max(f[toit[i]][0],f[toit[i]][1]+len[i]));
if (-max(f[toit[i]][0],f[toit[i]][1]+len[i])+f[toit[i]][0]+len[i] == fb) key = sb; else key = fb;
dp[now][1].push_back(tmp + key); best[now].push_back(key);
}
}
inline void work(int now,int id)
{
f[now][0] = dp[now][0][id];
f[now][1] = dp[now][1][id];
if (father[now])
{
f[now][0] += max(f[father[now]][0],f[father[now]][1]+cost[now]);
f[now][1] = f[now][0];
int key = max(best[now][id],-max(f[father[now]][0],f[father[now]][1]+cost[now])+f[father[now]][0]+cost[now]);
f[now][1] += key;
}
update(ans,f[son[now][id]][0]+max(f[now][0],f[now][1]+cost[son[now][id]]));
}
inline void Dp(int now)
{
for (int nn = son[now].size(),i = 0;i < nn;++i)
work(now,i),Dp(son[now][i]);
}
int main()
{
freopen("3677.in","r",stdin);
freopen("3677.out","w",stdout);
scanf("%d",&n);
for (int i = 1,a,b,c;i < n;++i) scanf("%d %d %d",&a,&b,&c),ins(a,b,c);
dfs(1); update(ans,f[1][0]);
Dp(1);
printf("%d",ans);
fclose(stdin); fclose(stdout);
return 0;
}
BZOJ 3677 连珠线的更多相关文章
- [Bzoj3677][Apio2014]连珠线(树形dp)
3677: [Apio2014]连珠线 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 434 Solved: 270[Submit][Status] ...
- bzoj3677: [Apio2014]连珠线
Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的 ...
- 【BZOJ3677】[Apio2014]连珠线 换根DP
[BZOJ3677][Apio2014]连珠线 Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色 ...
- 【LG3647】[APIO2014]连珠线
[LG3647][APIO2014]连珠线 题面 洛谷 题解 首先考虑一下蓝线连起来的情况,一定是儿子-父亲-另一个儿子或者是儿子-父亲-父亲的父亲. 而因为一开始只有一个点在当前局面上,将一条红边变 ...
- 题解 [APIO2014]连珠线
题解 [APIO2014]连珠线 题面 解析 首先这连成的是一棵树啊. 并且\(yy\)一下,如果钦定一个根, 那么这上面的蓝线都是爸爸->儿子->孙子这样的,因为像下图这样的构造不出来: ...
- bzoj 3677: [Apio2014]连珠线【树形dp】
参考:http://www.cnblogs.com/mmlz/p/4456547.html 枚举根,然后做树形dp,设f[i][1]为i是蓝线中点(蓝线一定是父子孙三代),f[i][0]为不是,转移很 ...
- BZOJ 1610 连线游戏
BZOJ不允许除以0. #include<iostream> #include<cstdio> #include<cstring> #include<cstd ...
- APIO2014 连珠线
题目链接:戳我 换根DP 由于蒟蒻不会做这个题,所以参考了大佬. 本来想的是有三种情况,一种是该节点不作为两个蓝线的中点(我们称这种不是关键节点),一种是该节点作为关键点.连两个子节点,一种是作为关键 ...
- Luogu P3647 [APIO2014]连珠线
题目 换根dp. 显然对于给定的一棵有根树,蓝线都不能拐弯. 设\(f_{u,0}\)表示\(u\)不是蓝线中点时子树内的答案,\(f_{u,1}\)表示\(u\)是蓝线中点时子树内的答案.(以\(1 ...
随机推荐
- C语言中volatilekeyword的作用
一.前言 1.编译器优化介绍: 由于内存訪问速度远不及CPU处理速度,为提高机器总体性能,在硬件上引入硬件快速缓存Cache,加速对内存的訪问.另外在现代CPU中指令的运行并不一定严格依照顺序运行,没 ...
- JS获取和设置光标的位置
<html> <head> <script language="javascript"> function getCursortPosition ...
- android86 监听SD卡状态,勒索软件,监听应用的安装、卸载、更新,无序广播有序广播
* 添加权限 <uses-permission android:name="android.permission.RECEIVE_SMS"/> * 4.0以后广播接收者 ...
- Qt学习之路: 国际化(上)
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://devbean.blog.51cto.com/448512/244689 2D ...
- ubuntu 14.04/15.10 安装基于eclipse的android app开发环境
一开始是装了ubuntu15.10,不知道是我的x200机器太old还是iso镜像有问题,总是各种莫名的引导不起来.有时候刚刚装好的干净系统,只install了一个vim和openssh,重启,然后就 ...
- Eclipse - 添加 PyDev 插件
1. 安装PyDev插件 启用Eclipse.在Help菜单中,选择Install New Software···, 然后点击Add按钮.在Location中输入:http://pydev.org/u ...
- 禁止输出重定向(>)覆盖已存在文件(防止误操作)
在输出重定向中,>表示重定向并覆盖已有文件内容,为了防止误操作,覆盖重要的内容,可以使用如下命令: set -C 这样输出重定向到一个已有文件就会提示: cannot overwrite exi ...
- Node.js + Express + Mongodb 开发搭建个人网站(三)
三.后台架构 1.在根目录下(和 views 文件夹同级)创建 lib 文件夹 以后所有后端内容 都是在这里写,分别创建三个文件夹 到 lib 目录下: mongo 放的是数据的存储 module ...
- A题笔记(10)
No.1390 代码:https://code.csdn.net/snippets/191965 另一版本:https://code.csdn.net/snippets/192009 考察点有两个: ...
- 基于url拦截实现权限控制
用户表,角色表,用户角色表,权限表,权限角色表 1.用户通过认证(可以是验证用户名,密码等) 2.登陆拦截器,为公开的url放行, 登陆时,将用户信息放入session中,获得用户的权限集合,将集合放 ...