洛谷 P3647 [APIO2014]连珠线(换根 dp)
题意:
桌子上有 \(1\) 个珠子,你要进行 \(n-1\) 次操作,每次操作有以下两种类型:
- 拿出一个新珠子,并选择一个桌子上的珠子,在它们之间连一条红线
- 选择两个由红线相连的珠子 \(u,v\),并拿出一个新珠子 \(w\),将原来连接 \(u,v\) 的红线断开,并在 \(u,w\) 和 \(v,w\) 之间各连一条蓝线。
显然最后 \(n\) 个珠子会形成一棵由 \(n-1\) 条线的树,给出最终每条线的长度,但你不知道它们的颜色。
你的得分为所有蓝线长度之和,求在所有可能的情况下,你得分的最大值。
\(n \in [2,2\times 10^5]\)
考虑蓝线的性质。由于连成的蓝线就没办法再被断开了,故蓝线连接的三个节点 \(u,w,v\) 在最终的树上也是相邻的。
故原题可以转化为:你可以选择一条三个节点 \(u\to v\to w\) 的链满足边 \((u,v),(v,w)\) 都没被选择,要求选出的边的权值之和的最大值。
树上相邻的三个节点有两种可能,一是爷爷->父亲->儿子,二是儿子->父亲->兄弟。
第二种情况比较复杂。不过我们可以枚举最开始的珠子在最终的树上的编号是多少,也就是钦定一个根节点,这样就不会出现第二种情况(很好理解,如果出现儿子->父亲->兄弟的情况,那我们肯定是先连好儿子->兄弟的边,再插入父亲节点,而由于父亲节点与根节点相连,所以应当是父亲节点先连好才对,所以这种情况不可能出现)
然后就可以 \(dp\) 了。\(dp_{u,0}\) 选好了 \(u\) 的子树中的边,\(u\) 不是某条蓝链的中点的最大权值和。\(dp_{u,1}\) 表示 \(u\) 是某条蓝链的中点的最大权值和。
考虑转移,\(dp_{u,0}\) 比较简单,枚举它的每个儿子 \(v\),有两种情况,要么 \((u,v)\) 间连了条红线,也就是 \(dp_{v,0}\),要么 \((u,v)\) 间连了条蓝线,而由于 \(u\) 不是某条蓝链的中点,所以这条链的连法只可能是 \(u\to v\to v\) 的某个儿子,也就是 \(dp_{v,1}+w\),故 \(dp_{u,0}=\sum\limits_{v\in son_u}\max(dp_{v,0},dp_{v,1}+w)\)
再考虑 \(dp_{u,1}\),显然 \(dp_{u,1}\) 是在 \(dp_{u,1}\) 是在 \(dp_{u,0}\) 的基础上将 \(u\) 与某个儿子 \(v\) 节点之间的边换成蓝边,考虑这一类树形 \(dp\) 的常用套路,枚举这个儿子 \(v\),计算将 \((u,v)\) 之间的边改为蓝边造成的 \(\Delta=dp_{v,0}+w-\max(dp_{v,0},dp_{v,1}+w)\),然后取个 \(\max\) 即可,故 \(dp_{u,1}=dp_{u,0}+\max\limits_{v\in son_u}dp_{v,0}+w-\max(dp_{v,0},dp_{v,1}+w)\)
然后考虑换根,记 \(f_{u}\) 表示以 \(u\) 为根节点的答案,\(out_{i,0/1}\) 表示 \(i\) 子树外的 \(dp\) 值,加法是可逆的,至于 \(\max\), multiset 维护一下即可。这一部分比较套路,具体见代码。
时间复杂度 \(\mathcal O(n\log n)\)
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
const int MAXN=2e5+5;
int n,to[MAXN<<1],nxt[MAXN<<1],hd[MAXN],cst[MAXN<<1],ec=0;
void adde(int u,int v,int w){
to[++ec]=v;nxt[ec]=hd[u];cst[ec]=w;hd[u]=ec;
}
int dp[MAXN][2],f[MAXN],out[MAXN][2];
void dfs(int x,int fa){
dp[x][1]=-2e9;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cst[e];if(y==fa) continue;dfs(y,x);
dp[x][0]+=max(dp[y][0],dp[y][1]+z);
dp[x][1]=max(dp[x][1],dp[y][0]+z-max(dp[y][0],dp[y][1]+z));
} dp[x][1]+=dp[x][0];
}
void cgrt(int x,int fa){
int sum=0;
multiset<int> st;st.insert(-2e9);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cst[e];
if(y==fa){
sum+=max(out[x][0],out[x][1]+z);
st.insert(out[x][0]+z-max(out[x][0],out[x][1]+z));
} else {
sum+=max(dp[y][0],dp[y][1]+z);
st.insert(dp[y][0]+z-max(dp[y][0],dp[y][1]+z));
}
}
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cst[e];if(y==fa) continue;
out[y][0]=sum-max(dp[y][0],dp[y][1]+z);
st.erase(st.find(dp[y][0]+z-max(dp[y][0],dp[y][1]+z)));
out[y][1]=out[y][0]+(*st.rbegin());
st.insert(dp[y][0]+z-max(dp[y][0],dp[y][1]+z));
f[y]=dp[y][0]+max(out[y][0],out[y][1]+z);
cgrt(y,x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);adde(v,u,w);
} dfs(1,0);f[1]=dp[1][0];cgrt(1,0);int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}
洛谷 P3647 [APIO2014]连珠线(换根 dp)的更多相关文章
- 洛谷$P3647\ [APIO2014]$连珠线 换根$dp$
正解:换根$dp$ 解题报告: 传送门! 谁能想到$9102$年了$gql$居然还没写过换根$dp$呢,,,$/kel$ 考虑固定了从哪个点开始之后,以这个点作为根,蓝线只可能是直上直下的,形如&qu ...
- 【BZOJ3677】[Apio2014]连珠线 换根DP
[BZOJ3677][Apio2014]连珠线 Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色 ...
- [换根DP]luogu P3647 [APIO2014]连珠线
题面 https://www.luogu.com.cn/problem/P3647 不重复地取树中相邻的两条边,每次得分为两条边权和,问最大得分 分析 容易想到状态 f[i][0/1] 分别表示 i ...
- Luogu P3647 [APIO2014]连珠线
题目 换根dp. 显然对于给定的一棵有根树,蓝线都不能拐弯. 设\(f_{u,0}\)表示\(u\)不是蓝线中点时子树内的答案,\(f_{u,1}\)表示\(u\)是蓝线中点时子树内的答案.(以\(1 ...
- 并不对劲的bzoj3677:p3647:[APIO2014]连珠线
题目大意 有一种生成\(n\)个点的树的方法为: 一开始有一个点,\(n-1\)次操作,每次可以有两种操作:1.选一个点,用一条红边将它与新点连接:2.将新点放在一条红边上,新点与这条红边两端点直接的 ...
- 【LG3647】[APIO2014]连珠线
[LG3647][APIO2014]连珠线 题面 洛谷 题解 首先考虑一下蓝线连起来的情况,一定是儿子-父亲-另一个儿子或者是儿子-父亲-父亲的父亲. 而因为一开始只有一个点在当前局面上,将一条红边变 ...
- BZOJ 3677 连珠线
Description 在达芬奇时代,有一个流行的儿童游戏称为连珠线.当然,这个游戏是关于珠子和线的.线是红色或蓝色的,珠子被编号为\(1\)到\(n\).这个游戏从一个珠子开始,每次会用如下方式添加 ...
- [Bzoj3677][Apio2014]连珠线(树形dp)
3677: [Apio2014]连珠线 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 434 Solved: 270[Submit][Status] ...
- 题解 [APIO2014]连珠线
题解 [APIO2014]连珠线 题面 解析 首先这连成的是一棵树啊. 并且\(yy\)一下,如果钦定一个根, 那么这上面的蓝线都是爸爸->儿子->孙子这样的,因为像下图这样的构造不出来: ...
随机推荐
- JavaScript04
分离绑定事件 使用分离方式绑定元素事件可以使用页面元素与JavaScript代码完全分离,有利于代码分工和维护,是目前开发主流,分为两步: 1.获取需要绑定事件的元素 语法:根据id属性值取元素节点 ...
- AgileConfig 轻量级配置中心 1.5 发布 - 支持多环境配置
AgileConfig 从发布到现在,收到不同学的 issue 说需要多环境的支持.也就是一个应用在不同的环境下可以配置不同的配置项.这是一个非常有用的功能,就跟我们开发的时候会设置多个 appset ...
- MySQL:怒刷牛客网“sql实战”
MySQL:怒刷牛客网"sql实战" 在对MySQL有一定了解后,抽空刷了一下 牛客网上的 数据库SQL 实战,在此做一点小小的记录 SQL1 查找最晚入职员工的所有信息 sele ...
- [no code][scrum meeting] Alpha 7
项目 内容 会议时间 2020-04-13 会议主题 OCR技术细节分析 会议时长 30min 参会人员 PM+OCR组成员 $( "#cnblogs_post_body" ).c ...
- java监控JVM的内存使用情况等
以下的程序监控参数的代码,有些是从网络上获取的,此处进行一个记录是为了以后如果要用到方便记录. 1.引入jar包,为了获取一些cpu的使用率等信息 <dependency> <gro ...
- FastAPI 学习之路(三十三)操作数据库
通过创建pydantic模型进行验证提交数据 from pydantic import BaseModel class UserBase(BaseModel): email: str class Us ...
- 【做题记录】CF1444A Division
CF1444A Division 题意: 给定 \(t\) 组询问,每组给两个数 \(p_i\) 和 \(q_i\) ,找出最大的整数 \(x_i\) ,要求 \(p_i\) 可被 \(x_i\) 整 ...
- Python import cStringIO ImportError: No module named 'cStringIO'
From Python 3.0 changelog; The StringIO and cStringIO modules are gone. Instead, import the io modul ...
- pl/sql 远程连接oracle数据库问题(TNS:丢失连接)
前几天还可以连接的,突然就连接失效,别的电脑也连接不上 1.ping 远程数据库的IP,可以ping通. 2.检查服务端的tnsnames.ora文件和本机客户端比较,没问题. 3.查看监听是否启动 ...
- 设计模式学习-使用go实现单例模式
单例模式 定义 优点 缺点 适用范围 代码实现 懒汉模式 饿汉模式 双重检测 sync.Once 参考 单例模式 定义 什么是单例模式:保证一个类仅有一个实例,并提供一个全局访问它的全局访问点. 例如 ...