题解 [APIO2014]连珠线

题面

解析

首先这连成的是一棵树啊.

并且\(yy\)一下,如果钦定一个根,

那么这上面的蓝线都是爸爸->儿子->孙子这样的,因为像下图这样的构造不出来:

(兄弟到兄弟的特殊情况不用考虑,因为会在一个端点作为根的情况考虑的)

那么首先还是来简单的写法,

设\(f[i][0/1]\)表示\(i\)是否为一根蓝线的中点的最大分数,

也可以理解为从\(i\)的一个儿子到\(i\)在上去还有没有蓝线.

并且,\(f[i][1]\)要算上它到父亲的边权.

然后再设\(c[i]\)=\(\max(f[i][0],f[i][1])\),

主要是懒得写

那么\(f[i][0]=\sum_{k=son[i]}c[k]\),

而\(f[i][1]=f[i][0]+\max(f[k][0]+w[k]-c[k])\),

其中\(w[k]\)表示\(k\)到父亲的边权(也就是i到k)

跑\(n\)遍dfs即可.

但这显然可以换根DP啊.

设\(dp[i]\)表示以\(i\)为根的最大分数,

\(v[i]\)表示\(i\)的父亲作为一条蓝边的中点,而\(i\)是一个端点的分数,并且也要再算上\(fa\)到\(i\)这条边.

(可以理解为f[fa][1]伸出去的那条边到了\(i\)这里)

那么有\(dp[i]=f[i][0]+max(dp[fa]-c[i],v[i])\)

就是\(i\)子树里的贡献加上父亲的贡献.

而父亲的贡献要么是不连边(\(dp[fa]-c[i]\)),要么就连边(v[i]).

(把\(f[i][0]\)式子里的\(c[k]\)换成\(c\)的定义就会发现很像)

然后考虑怎么求\(v\).

这里我们是用父亲去求儿子,

也就是当前是\(i\)时,我们考虑求\(i\)的儿子\(k\)(们)的\(v[k]\).

首先\(k\)是一个端点,那么我们要在\(i\)的儿子里再找出一个端点,

这里我们记一个\(mx1\)代表更新\(f[x][1]\)时后面那一串max(f[k][0]+w[k]-c[k])的最大值,

\(mx2\)表示次大值,\(id\)表示值为\(mx1\)的\(k\).

然后在求\(v[k]\)时,我们就有:

  • \(v[k]=dp[i]-c[k]+mx1+w[i]\),\(k\not=id\)

    这时我们可以直接拿最大值来贡献到\(k\)

  • \(v[k]=dp[i]-c[k]+mx2+w[i]\),\(k=id\)

    因为\(k\)已经是最大值的端点了,所以只能拿次大值来更新.

注意,\(mx1\)和\(mx2\)都要算上父亲!!!

显然父亲也会有贡献.

而父亲的贡献是dp[fa]-c[x]+w[i]-max(dp[fa]-c[x],v[x])

其实和上面的式子的结构是一样的(\(dp[fa]-c[x]\)就是\(f[k][0]\),\(\max(dp[fa]-c[x],v[x])\)就是\(c\))

然后就没有然后了

code:


#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std; inline int read(){
int sum=0,f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0'){sum=(sum<<3)+(sum<<1)+c-'0';c=getchar();}
return sum*f;
} const int N=200005;
const int INF=1e18;
struct edge{int to,next,w;}e[N<<1];
struct node{int mx1,mx2,id;}a[N];
int n;
int f[N][2],c[N],v[N],dp[N];
int head[N],cnt=0; inline void add(int x,int y,int w){
e[++cnt]=(edge){head[x],y,w};head[x]=cnt;
} inline void dfs(int x,int fa){
int ok=0;
for(int i=head[x];i;i=e[i].to){
int k=e[i].next;
if(k==fa) continue;
f[k][1]+=e[i].w;
dfs(k,x);ok=1;
f[x][0]+=c[k];
if(f[k][0]+e[i].w-c[k]>a[x].mx1)
a[x].mx2=a[x].mx1,a[x].mx1=f[k][0]+e[i].w-c[k],a[x].id=k;
else if(f[k][0]+e[i].w-c[k]>a[x].mx2)
a[x].mx2=f[k][0]+e[i].w-c[k];
}
f[x][1]+=f[x][0]+a[x].mx1;
if(!ok) f[x][1]=-INF;
c[x]=max(f[x][0],f[x][1]);
} inline void dfs1(int x,int fa){
dp[x]=f[x][0]+max(dp[fa]-c[x],v[x]);
for(int i=head[x];i;i=e[i].to){
int k=e[i].next;
if(k==fa) continue;
if(k==a[x].id) v[k]=dp[x]-c[k]+a[x].mx2+e[i].w;
else v[k]=dp[x]-c[k]+a[x].mx1+e[i].w;
int ret=dp[x]-c[k]+e[i].w-max(dp[x]-c[k],v[k]);
if(ret>a[k].mx1) a[k].mx2=a[k].mx1,a[k].mx1=ret,a[k].id=x;
else if(ret>a[k].mx2) a[k].mx2=ret;
dfs1(k,x);
}
} signed main(){
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read(),w=read();
add(x,y,w);add(y,x,w);
}
for(int i=1;i<=n;i++) a[i].mx1=a[i].mx2=-INF;
dfs(1,0);dfs1(1,0);
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
printf("%lld\n",ans);
return 0;
}

题解 [APIO2014]连珠线的更多相关文章

  1. 【BZOJ3677】[Apio2014]连珠线 换根DP

    [BZOJ3677][Apio2014]连珠线 Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色 ...

  2. 【LG3647】[APIO2014]连珠线

    [LG3647][APIO2014]连珠线 题面 洛谷 题解 首先考虑一下蓝线连起来的情况,一定是儿子-父亲-另一个儿子或者是儿子-父亲-父亲的父亲. 而因为一开始只有一个点在当前局面上,将一条红边变 ...

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

    3677: [Apio2014]连珠线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 434  Solved: 270[Submit][Status] ...

  4. bzoj3677: [Apio2014]连珠线

    Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的 ...

  5. APIO2014 连珠线

    题目链接:戳我 换根DP 由于蒟蒻不会做这个题,所以参考了大佬. 本来想的是有三种情况,一种是该节点不作为两个蓝线的中点(我们称这种不是关键节点),一种是该节点作为关键点.连两个子节点,一种是作为关键 ...

  6. 并不对劲的bzoj3677:p3647:[APIO2014]连珠线

    题目大意 有一种生成\(n\)个点的树的方法为: 一开始有一个点,\(n-1\)次操作,每次可以有两种操作:1.选一个点,用一条红边将它与新点连接:2.将新点放在一条红边上,新点与这条红边两端点直接的 ...

  7. bzoj 3677: [Apio2014]连珠线【树形dp】

    参考:http://www.cnblogs.com/mmlz/p/4456547.html 枚举根,然后做树形dp,设f[i][1]为i是蓝线中点(蓝线一定是父子孙三代),f[i][0]为不是,转移很 ...

  8. Luogu P3647 [APIO2014]连珠线

    题目 换根dp. 显然对于给定的一棵有根树,蓝线都不能拐弯. 设\(f_{u,0}\)表示\(u\)不是蓝线中点时子树内的答案,\(f_{u,1}\)表示\(u\)是蓝线中点时子树内的答案.(以\(1 ...

  9. 洛谷$P3647\ [APIO2014]$连珠线 换根$dp$

    正解:换根$dp$ 解题报告: 传送门! 谁能想到$9102$年了$gql$居然还没写过换根$dp$呢,,,$/kel$ 考虑固定了从哪个点开始之后,以这个点作为根,蓝线只可能是直上直下的,形如&qu ...

随机推荐

  1. sql复合索引使用和注意事项

    1.定义: 单一索引: 单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上; 复合索引: 复合索引也叫组合索引: 用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引). 复合索引在 ...

  2. java积累的细节问题

    一.将几个数字进行拼接,拼接成字符串 一定要将""放到几个数字之前,否则就会把数字相加之后的数转化成字符串 如你需要拼接年份,月份,和日 如果:2018+12+13+"& ...

  3. RabbitMq的环境安装

    1.如图第一个是erlang语言的安装包,第二个是rabbitmq的安装包. 2.配置erlang语言环境,因为rabbitmq由erlang语言编写的,所以需要配置erlng语言环境. erlang ...

  4. jvm的内存区域介绍

    什么是jvm? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的 ...

  5. C#基础--go to

    goto语句的用法非常灵活,你可以用它实现很多功能,但是由于goto语句的跳转影响程序的结构,在使用的时候会使人迷茫,所以一般"教材"上都不建议使用,但是用它可以实现递归,循环,选 ...

  6. JDBC 复习2 存取mysql 大数据

    大数据也称之为LOB(Large Objects),LOB又分为:clob和blob,clob用于存储大文本,blob用于存储二进制数据 mysql的大数据分为2种 blob 和 text ,没有cl ...

  7. java封装数据类型——Integer 缓存策略验证

    上一篇学习 Integer 类型源码,知道了它使用缓存策略,默认对 [-128, 127] 范围的对象进行类加载时自动创建缓存. Integer 源码学习:https://www.cnblogs.co ...

  8. SIP:100rel 扩展

    SIP:100rel 扩展 100rel扩展即是对中间状态响应的确认(即1xx的响应码).原先在sip里,只有针对invite请求的200ok响应才会有ack,那么当中间状态响应携带重要的会话参数信息 ...

  9. JS做2048

    首先我们了解一下2048这个游戏的原理: 他由一个4x4二维数组组成,在游戏一开始时候在随机位置随机生成一个2或者4 如: 1.每点击一次开始就刷新一次游戏界面: 2.通过键盘的上下左右四个方向键分别 ...

  10. OpenCV实现图象翻转、滤波、锐化

    OpenCV实现图象翻转.滤波.锐化 注:以下代码,使用opencv库函数实现了对图片的翻转.灰度图转换.各种滤波.各种锐化. 库函数相关参数及说明参阅:OpenCV中文站=>opencv教程( ...