[BZOJ3677][Apio2014]连珠线

试题描述

在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”。不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色。游戏开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入:
1.Append(w,v):一个新的珠子w和一个已有的珠子v连接,连接使用红线。 
2.Insert(w,u,v):一个新的珠子w加入到一对通过红线连接的珠子(u,v)之间,并将红线改成蓝线。也就是将原来u连到1的红线变为u连到w的蓝线与W连到V的蓝线。 
无论红线还是蓝线,每条线都有一个长度。而在游戏的最后,将得到游戏的最后得分:所有蓝线的长度总和。 
现在有一个这个游戏的最终结构:你将获取到所有珠子之间的连接情况和所有连线的长度,但是你并不知道每条线的颜色是什么。 
你现在需要找到这个结构下的最大得分,也就是说:你需要给每条线一个颜色f红色或蓝色),使得这种连线的配色方案是可以通过上述提到的两种连线方式操作得到的,并且游戏得分最大。在本题中你只需要输出最大的得分即可。

输入

第一行是一个正整数n,表示珠子的个数,珠子编号为1刭n。 
接下来n-l行,每行三个正整数ai,bi(l≤ai≤10000),表示有一条长度为ci的线连接了珠子ai和珠子bi。

输出

输出一个整数,为游戏的最大得分。

输入示例

5
1 2 10
1 3 40
1 4 15
1 5 20

输出示例

60

数据规模及约定

数据范围满足1≤n≤200000。

题解

一个恶心至极的树形 dp。

我的做法大概是设状态为 f(i, j, k),i 表示我们在研究子树 i,其中 j 取 0 或 1;j = 0 时表示根在子树 i(注意这里的子树 i 都不包括节点 i)外,此时 k = 0 表示 i 不是中心点,k = 1 表示 i 是中心点;j = 1 时表示根在子树 i 中,此时 k = 0 表示 i 不是中心点,k = 1 表示 i 是中心点并且两条蓝边都在原树的子树 i 中,k = 2 表示 i 是中心点且有一条蓝边在原树的子树 i 中,另一条为 i 连向原树父亲的边。

这个 dp 不用换根,随便选一个点 rt 开始 dp 就好了,注意最后答案是 max{ f(rt, 0, 0), f(rt, 1, 0), f(rt, 1, 1) },没有 f(rt, 0, 1) 和 f(rt, 1, 2)(想一想为什么)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxn 200010
#define maxm 400010
#define oo (1ll << 50)
#define LL long long

int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm];

void AddEdge(int a, int b, int c) {
	to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
	return ;
}

LL f[maxn][2][3];
void dp(int u, int fa) {
	LL sum = 0, m1x = -oo, m00 = -oo, m1x_1 = -oo, m1x_2 = -oo, m00_1 = -oo, m00_2 = -oo;
	int m1x_1id = -1, m00_1id = -1;
	bool has = 0;
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
		has = 1;
		dp(to[e], u);
		LL tmp = max(f[to[e]][0][0], f[to[e]][0][1] > -oo ? f[to[e]][0][1] + dist[e] : 0);
		sum += tmp;
		if(f[to[e]][1][0] > -oo) m1x = max(m1x, max(max(f[to[e]][1][0], f[to[e]][1][1]), f[to[e]][1][2] + dist[e]) - tmp);
		m00 = max(m00, f[to[e]][0][0] - tmp);
		LL t1x = max(f[to[e]][1][0], f[to[e]][1][1]) + dist[e];
		if(f[to[e]][1][0] > -oo) {
			if(t1x - tmp > m1x_1) m1x_2 = m1x_1, m1x_1 = t1x - tmp, m1x_1id = to[e];
			else if(t1x - tmp > m1x_2) m1x_2 = t1x - tmp;
		}
		int t00 = f[to[e]][0][0] + dist[e];
		if(t00 - tmp > m00_1) m00_2 = m00_1, m00_1 = t00 - tmp, m00_1id = to[e];
		else if(t00 - tmp > m00_2) m00_2 = t00 - tmp;
	}
	if(!has) {
		f[u][0][1] = f[u][1][0] = f[u][1][1] = f[u][1][2] = -oo;
		return ;
	}
	f[u][0][0] = sum;
	if(m00_2 > -oo || fa) f[u][0][1] = sum + m00_1;
	f[u][1][0] = sum + max(m00, m1x);
	if(m00_2 > -oo) f[u][1][1] = sum + max(m00_1 + m00_2, max(m00_1 + (m00_1id == m1x_1id ? m1x_2 : m1x_1), m1x_1 + (m1x_1id == m00_1id ? m00_2 : m00_1)));
	if(fa) f[u][1][2] = sum + max(m00_1, m1x_1);
//	printf("%d: %d %d | %d %d %d\n", u, f[u][0][0], f[u][0][1], f[u][1][0], f[u][1][1], f[u][1][2]);
	return ;
}

int main() {
	n = read();
	for(int i = 1; i < n; i++) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c);
	}

	dp(1, 0);

	printf("%d\n", (int)max(max(f[1][1][0], f[1][1][1]), f[1][0][0]));

	return 0;
}
时间: 2024-10-18 22:10:33

[BZOJ3677][Apio2014]连珠线的相关文章

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

3677: [Apio2014]连珠线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 434  Solved: 270[Submit][Status][Discuss] Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入: 1.Append(w,杪):-个新的珠子w

【BZOJ 3677】 [Apio2014]连珠线

3677: [Apio2014]连珠线 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 119 Solved: 60 [Submit][Status][Discuss] Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做"连珠线".不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入: 1.Append(w,杪):-个新

APIO2014 连珠线

题目链接:戳我 换根DP 由于蒟蒻不会做这个题,所以参考了大佬. 本来想的是有三种情况,一种是该节点不作为两个蓝线的中点(我们称这种不是关键节点),一种是该节点作为关键点.连两个子节点,一种是作为关键节点.一个连子节点一个连父亲节点. 然后有一个不换根的树形DP,但是正确性emmm尚待商榷. 对于一个这样的图-- 我们可以发现,如果想要连起来的话,我们需要不止一个根节点,而这与题目中提到的每次加入一个节点不符. 所以我们考虑换根.这样的话我们发现,就只有两种情况了--一种是该节点不作为关键节点,

Luogu P3647 [APIO2014]连珠线

题目 换根dp. 显然对于给定的一棵有根树,蓝线都不能拐弯. 设\(f_{u,0}\)表示\(u\)不是蓝线中点时子树内的答案,\(f_{u,1}\)表示\(u\)是蓝线中点时子树内的答案.(以\(1\)为根的情况下) 那么显然有\(f_{u,0}=\sum\limits_{v\in son_u}\max(f_{v,0},f_{v,1}+d_v)\). (\(son_u\)表示\(u\)的儿子集合,\(d_u\)表示\((u,fa_u)\)的长度) 但是\(f_{u,1}\)如何求? 我们这样考

bzoj3677【APIO2014】连珠线

3677: [Apio2014]连珠线 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 204  Solved: 115 [Submit][Status][Discuss] Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做"连珠线".不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入: 1.Append(w,杪):

动态规划小练

终于意识到动规的水平差得一批了.这边开个小专题练一下. 题目列表是学长 frank_c1 的博客.这样一来可以保证质量,二来也有自己能看懂的题解. 事实上很多题意也是贺的 这里还是挂个链接吧:orz (刷新以获取数学公式) [APIO 2014] Beads and wires 题意 在达芬奇时代,有一个流行的儿童游戏称为连珠线.当然,这个游戏是关于珠子和线的.线是红色或蓝色的,珠子被编号为 $ 1 $ 到 $ n $ .这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子: $ Appen

基于HTML5实现五彩连珠小游戏

今天给大家分享一款基于HTML5实现五彩连珠小游戏.这款游戏的规则:点击彩球移动到期望的位置,每移动一次,画面将随机出现3个新的彩球:当同一颜色的彩球连成5个一行或一列或一斜线时,这5个彩球同时消失,游戏得分10分.当画面上每个方格都被彩球占满时,游戏结束. 在线预览   源码下载 实现的代码. html代码: <canvas id="canvas" height="400" width="600" style="backgrou

线扫相机项目中定量判断机构运动是否匀速的方法

线扫相机的原理:线扫相机一般一次只拍摄一条线(线宽通常是1个像素),在机构运动的过程中,线扫相机不断地拍摄线,于是“聚线成面”,这就是线扫相机成像的原理. 线扫相机的原理决定了,它所拍摄的物体必须要运动.机构运动的话,就存在机构的速度是否和线扫相机采集的速度匹配的问题. 将对机构运动速度的要求进行拆分的话,其实就是如下两点: ① 运动速度必须与线扫相机的工作行频(即采集速度)相匹配. ② 机构的运动速度最好是匀速,或者十分接近匀速. 如果机构速度过快,最后的图像在运动方向上被压缩:如果机构速度过

半截水晶头线接激活本地虚拟网卡

可有可无的小方法~ 很多人在用虚拟机的时候,有时虚拟网卡不够用,又没有可用网线插来激活本地网卡时,这时可以自制一个水晶头加半截网线(或者从水晶头还 能用的废掉的网线上截取下来如下图)来激活本地网卡,多增加一个可用虚拟网卡(虽然第一次在当时无法解决(^-^)) 制作方法:截取或制作成水晶头后,将线皮剥掉至漏出铜线,将线序颜色排列成T-568B的线序(如下图) 以10/100兆太网网卡为例的DTE类型接口引脚定义为(对应上图从左到右): 1-TX+Tranceive  Data+  (发信号+)