题意

传送门

题解

我们发现,如果一棵树的形态固定了,那么蓝线的方向一定是son[x]-x-fa[x],那么我们就可以先随便定一个根进行DP。

我们设f[i][0]f[i][0]f[i][0]表示以iii为根的子树中,且iii不作为蓝线的中点能够得到的最大价值。同理,设f[i][1]f[i][1]f[i][1]表示以iii为根的子树中,iii作为蓝线的中点能够得到的最大价值。

我们分别对于两种情况分析。f[i][0]f[i][0]f[i][0]较为简单,对于一个j∈son[i]j\in son[i]j∈son[i],设wjw_jwj​为i,ji,ji,j之间的边权,要使iii不作为蓝线的中点,则满足jjj为蓝线中点(即f[j][1]+wjf[j][1]+w_jf[j][1]+wj​),或之间是红线(即f[j][0]f[j][0]f[j][0])。所以f[i][0]=∑j∈son[i]max⁡(f[j][0],f[j][1]+wj)f[i][0] = \sum\limits_{j\in son[i]}\max(f[j][0], f[j][1]+w_j)f[i][0]=j∈son[i]∑​max(f[j][0],f[j][1]+wj​)。

然后考虑iii为中点的情况,显然iii只能是一条蓝线的中点,所以我们可以枚举这条蓝线连接的儿子(设为jjj),那么其余儿子依旧是按照f[i][0]f[i][0]f[i][0]的方式转移,所以我们将f[i][1]f[i][1]f[i][1]初始化为f[i][0]f[i][0]f[i][0]。对于jjj,我们减去之前的贡献,再加入蓝线的贡献。由于iii是中点,所以jjj的贡献即为f[j][0]+wjf[j][0]+w_jf[j][0]+wj​。综上,f[i][1]=f[i][0]+max⁡j∈son[i](f[j][0]+wj−max⁡(f[j][0],f[j][1]+wj))f[i][1]=f[i][0]+\max\limits_{j\in son[i]}(f[j][0]+w_j-\max(f[j][0],f[j][1]+w_j))f[i][1]=f[i][0]+j∈son[i]max​(f[j][0]+wj​−max(f[j][0],f[j][1]+wj​))。

说了这么多,其实都只是在树的结构固定的前提下进行的。当整棵树的结构不确定时,我们就需要通过换根操作统计答案。我们发现换根对于大部分节点并没有影响,于是我们就可以通过一些奇技淫巧进行O(1)O(1)O(1)换根。

我们考虑一个点的儿子变成了父亲会发生什么影响。首先这个儿子的贡献消失了,随之而来的可能是转移方程中的最大值也消失了,所以我们就需要记录次大值(经典套路)。同时当前点会变成儿子对原来的儿子(现在的父亲)产生贡献。

所以我们要在第一次DP中记录一个dp[i][0/1][j]dp[i][0/1][j]dp[i][0/1][j]表示在f[i][0/1]f[i][0/1]f[i][0/1]这个状态的统计过程中,不考虑第jjj个儿子得到的答案。对于dp[i][0][j]dp[i][0][j]dp[i][0][j]直接从总和中减去;对于dp[i][1][j]dp[i][1][j]dp[i][1][j],维护次大值更新即可。

下面正式开始换根,我们在dfsdfsdfs过程中,枚举当前节点xxx的儿子作为整棵树的根,此时值得注意的是,由于换根后,xxx的父亲会变成他的儿子,所以我们并不能直接在xxx和儿子之间换根,而是应该先重新计算fa[x]fa[x]fa[x]对xxx的贡献,然后再进行换根。具体操作可以看代码,十分容易理解。

代码

#include <bits/stdc++.h>
#define MAX 500005
#define INF 0x3f3f3f3f
#define c(x) (f[x][0]+cost[i]-max(f[x][0], f[x][1]+len[x]))       //状态转移方程
using namespace std;int n, cnt;
int head[MAX], vet[MAX], Next[MAX], cost[MAX];void add(int x, int y, int w){cnt++;Next[cnt] = head[x];head[x] = cnt;vet[cnt] = y;cost[cnt] = w;
}int par[MAX], len[MAX];
int f[MAX][2];       //f[i][0]表示i不做中点,f[i][1]表示做中点
vector<int> son[MAX], dp[MAX][2], mx[MAX];
void dfs(int x, int fa){f[x][0] = 0, f[x][1] = -INF;int mx1 = -INF, mx2 = -INF;for(int i = head[x]; i; i = Next[i]){int v = vet[i];if(v == fa) continue;len[v] = cost[i], par[v] = x;son[x].push_back(v);dfs(v, x);f[x][0] += max(f[v][0], f[v][1]+cost[i]);if(c(v) > mx1) mx2 = mx1, mx1 = c(v);        //记录最大值和次大值else if(c(v) > mx2) mx2 = c(v);}f[x][1] = f[x][0]+mx1;for(int i = head[x]; i; i = Next[i]){int v = vet[i];if(v == fa) continue;dp[x][0].push_back(f[x][0]-max(f[v][0], f[v][1]+cost[i]));if(c(v) == mx1){      //通过最大值和次大值计算dp[x][1]dp[x][1].push_back(dp[x][0].back()+mx2);mx[x].push_back(mx2);}else{dp[x][1].push_back(dp[x][0].back()+mx1);mx[x].push_back(mx1);}}
}int ans = 0;
void solve(int x){     //换根for(int i = 0; i < son[x].size(); i++){f[x][0] = dp[x][0][i], f[x][1] = dp[x][1][i];if(par[x]){     //重新统计父亲对x的贡献。f[x][0] += max(f[par[x]][0], f[par[x]][1]+len[x]);f[x][1] = f[x][0] + max(mx[x][i], f[par[x]][0]+len[x]-max(f[par[x]][0], f[par[x]][1]+len[x]));}ans = max(ans, f[son[x][i]][0]+max(f[x][0], f[x][1]+len[son[x][i]]));solve(son[x][i]);}
}int main()
{cin >> n;int x, y, w;for(int i = 1; i < n; i++){scanf("%d%d%d", &x, &y, &w);add(x, y, w);add(y, x, w);}dfs(1, 0);solve(1);cout << ans << endl;return 0;
}

P3647 [APIO2014]连珠线相关推荐

  1. 洛谷P3647 [APIO2014] 连珠线 题解

    洛谷P3647 [APIO2014] 连珠线 题解 题目链接:P3647 [APIO2014] 连珠线 题意: 在达芬奇时代,有一个流行的儿童游戏称为连珠线.当然,这个游戏是关于珠子和线的.线是红色或 ...

  2. luogu P3647 [APIO2014] 连珠线

    https://www.luogu.com.cn/problem/P3647 我写DP像cxk 发现只有两种连边的方式 假设最优的策略已经给了出来,那么一定存在选定某个点为根的时候,只存在第一种脸边的 ...

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

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

  4. [APIO2014]连珠线

    题目链接 分析 首先考虑朴素算法. 可以枚举每个节点作为根,这样的话连接的树的形态就确定了. 应为我们可以知道每个点只可能是一个蓝边的中点,不难想到设一个DP状态表示节点是否为蓝边的中点,设 dp[i ...

  5. APIO2014 连珠线

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

  6. [APIO2014]连珠线 题解

    我们设初始的那个点为root.则所有的蓝色链都是形如father−now−sonfather-now-sonfather−now−son. 我们设计两个dp状态: dpi,0dp_{i,0}dpi,0 ...

  7. BZOJ3677: [Apio2014]连珠线

    这题刚了好久的treedp,一直WA比较大的点,不知道哪里错了以为我想法错了看题解结果都说换根好做些不换根很多特殊情况-. 咱觉得咱的想法没问题..,应该是一些细节没考虑好,又想了想一些反例就过去了 ...

  8. [BZOJ3677/UOJ#105][APIO2014]Beads and wires 连珠线(树形dp+换根)

    Address https://www.lydsy.com/JudgeOnline/problem.php?id=3677 http://uoj.ac/problem/105 Solution 考虑一 ...

  9. 红黑树如何快速调整到平衡态_快速多态

    红黑树如何快速调整到平衡态 进阶主题 (Advanced Topic) Object-Oriented Programming has ruled the programming world for ...

最新文章

  1. 计算机书籍- 网络爬虫开发实战
  2. 除了 iOS 和 Android,世界第三大移动系统是什么?
  3. DeepMind难以盈利,人工智能该走向何处去?
  4. idea怎么配置spring
  5. 【五线谱】Sibelius 7.5.1 打谱软件安装 ( 软件下载 | 软件安装 )
  6. [转]linux tr命令详解
  7. 3x3九宫格java_Java实现九宫格的简单实例
  8. Spring连接mysql数据库错误:Cannot load JDBC driver class '${driver}'
  9. php静态属性和普通属性吗,php中静态属性和普通属性的区别
  10. OpenCV学习笔记02:OpenCV基本图片处理
  11. 你(wo)不注意的和数据类型有关的小细节
  12. [转]Linux 基本操作(RM 删除)
  13. 【图像增强】基于matlab GUI暗通道图像去雾【含Matlab源码 835期】
  14. 微信小程序官方开发文档
  15. 联众打码写滑动_自己写了一个答题的软件现在分享一下(在分享一下联众打码源码调用) _ 综合讨论 - 按键精灵论坛...
  16. 联想服务器如何u盘安装系统教程,联想lenovo u盘安装Windows 7系统操作步骤详解
  17. Eclipse简明使用教程(java集成开发环境)
  18. 常见字符的ASCII码值
  19. 世界和平 - 中国国际友好城市数据集
  20. 红蜘蛛显示器测试软件,红蜘蛛5使用displayCAL校准显示器(蓝绿蜘蛛5通用)

热门文章

  1. 双链路热备份(负载分担)实验
  2. android仿今日头条视频显示效果
  3. php 生成国税局二维码
  4. 01.JS基础_前端的语法(4)
  5. FFmpeg屏幕录制
  6. iOS UIAppearance使用详解
  7. Gym - 101853E E. Maximum Sum (状压DP)
  8. 精华QT安装:qt-opensource-windows-x86-5.14.1
  9. 立创EDA学习笔记(3)——PCB绘制
  10. 史上最详细的新浪广告系统技术架构优化历程