洛谷 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\)一下,如果钦定一个根, 那么这上面的蓝线都是爸爸->儿子->孙子这样的,因为像下图这样的构造不出来: ...
随机推荐
- 【UE4 设计模式】状态模式 State Pattern
概述 描述 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式. 有限状态机(FSMs) ...
- UI自动化测试之Airtest
官方文档: https://airtest.doc.io.netease.com/ 本文我们讲解下Airtest的使用,主要学习目标有以下几点: (1)认识Airtest (2)了解Airtest能做 ...
- 权限管理RBAC模型概述
一.什么是RBAC模型 RBAC模型(Role-Based Access Control:基于角色的访问控制)模型是比较早期提出的权限实现模型,在多用户计算机时期该思想即被提出,其中以美国George ...
- 6月6日 Scrum Meeting
日期:2021年6月6日 会议主要内容概述: 删除模板选择页面,画图页面新增模板选择 保存时后端判重 后端要新增数据分享url 主题色->lxd:画图教程->lsp:表格数据分析-> ...
- Beta_Scrum Meeting_0
日期:2021年5月26日 参会人员:cy.hcc.lsc.dxh 会议主题:为Beta阶段最早两日的开发制定目标 一.进度情况 组员 负责 两日内完成的任务 接下来两日预计完成的任务 hcc 前端 ...
- 替换excel模板中的内容并使用JavaMail发送邮件
由于在公司工作,常年出差,每天都要以日报的形式向公司汇报当天的工作内容.而日报的内容大体上就只有当天工作的主要内容时变化的,其余的都是不变 的. 而我的电脑刚打开excel有点卡,因此决定使用Java ...
- CCD摄像头视场角计算公式
视场角大小和CCD传感器尺寸和镜头焦距有关: 水平视场角 = 2 × arctan(w / 2f); 垂直视场角 = 2 × arctan(h / 2f); 视场角 = 2 × arctan(d / ...
- 洛谷 P3195 [HNOI2008] 玩具装箱
链接: P3195 题意: 给出 \(n\) 个物品及其权值 \(c\),连续的物品可以放进一个容器,如果将 \(i\sim j\) 的物品放进一个容器,产生的费用是 \(\left(j-i+\sum ...
- [CSP-S2021] 括号序列
链接: P7914 题意: 有一堆规则,然后判断给定字符串有多少种填法符合规则. 分析: 一眼区间dp,状态数 \(n^2\),我们来分析这些规则. 把这些规则分成三类,第一类可以预处理出区间是否能表 ...
- 利用Microsoft Graph开发微软的Exchange Calendar会议、Team
Microsoft Graph是什么? Microsoft Graph 是 Microsoft 365 中通往数据和智能的网关. 它提供统一的可编程模型,可用于访问 Microsoft 365.Win ...