[八省联考 2018] 林克卡特树 题解
这道题我前前后后做了一年,共过了 4 4 4 遍,每次都有的新的理解;这次我认为自己理解透了,于是就写了一篇题解。
这道题是我入坑看到的第一道黑题(当时很萌,不知道黑题是什么,看到这题感觉很好玩),另外还有就是《切树游戏》和 Spiders Evil Plan,记载着我的回忆(
Description
传送门
Solution
算法一
为方便叙述,令树根为 1 1 1, w u , v w_{u,v} wu,v 表示 u , v u,v u,v 之间的边权, son { u } \text{son}\{u\} son{u} 表示 u u u 的儿子组成的集合。
可以发现,答案即为 ⌈ \lceil ⌈ 在树上选出 k + 1 k+1 k+1 条不相交的路径 ⌋ \rfloor ⌋ 的最大边权和。
考虑 dp \text{dp} dp。
令 f u , i , j f_{u,i,j} fu,i,j 表示,看了以 u u u 为根的子树,子树内一共选了 i i i 条不相交的路径,且 u u u 当前度数为 j j j 的最大边权和。
- 若 j = 0 j=0 j=0,则目前没有包含 u u u 的路径。特别的,若以 u u u 为根的子树已经遍历完,则 f u , i , j f_{u,i,j} fu,i,j 表示所有该子树中的路径都被固定时的最大边权和(也就是说,遍历完子树后 f u , 0 f_{u,0} fu,0 对应的状态中 u u u 的度数可以为 0 0 0 或 1 1 1 或 2 2 2)。
- 若 j = 1 j=1 j=1,则目前恰有一条以 u u u 为一端的非固定路径(也就是说可以继续延伸出去,没有彻底固定这条路径的形态)。因为这条路径不固定,所以这条路径并没有被计入 i i i。
- 若 j = 2 j=2 j=2,则目前有一条包含 u u u 的路径,且其任意一端均不为 u u u(跨越了 u u u 的两个子树)。
考虑使用树形背包转移。具体来说,令目前要将以 v ( v ∈ son { u } ) v(v \in \text{son}\{u\}) v(v∈son{u}) 为根的子树合并上来,则有转移:
f u , i , 0 : = max ( f u , i , 0 , max j = 0 i { f u , j , 0 + f v , i − j , 0 } ) f_{u,i,0}:=\max(f_{u,i,0},\max_{j=0}^i \{f_{u,j,0}+f_{v,i-j,0}\}) fu,i,0:=max(fu,i,0,j=0maxi{fu,j,0+fv,i−j,0}) f u , i , 1 : = max ( f u , i , 1 , max j = 0 i { f u , j , 0 + f v , i − j , 1 + w u , v } , max j = 0 i { f u , j , 1 + f v , i − j , 0 } ) f_{u,i,1}:=\max(f_{u,i,1},\max_{j=0}^i \{f_{u,j,0}+f_{v,i-j,1}+w_{u,v}\},\max_{j=0}^i \{f_{u,j,1}+f_{v,i-j,0}\}) fu,i,1:=max(fu,i,1,j=0maxi{fu,j,0+fv,i−j,1+wu,v},j=0maxi{fu,j,1+fv,i−j,0}) f u , i , 2 : = max ( f u , i , 2 , max j = 0 i − 1 { f u , j , 1 + f v , i − j − 1 , 1 + w u , v } , max j = 0 i { f u , j , 2 + f v , i − j , 0 } ) f_{u,i,2}:=\max(f_{u,i,2},\max_{j=0}^{i-1} \{f_{u,j,1}+f_{v,i-j-1,1}+w_{u,v}\},\max_{j=0}^i \{f_{u,j,2}+f_{v,i-j,0}\}) fu,i,2:=max(fu,i,2,j=0maxi−1{fu,j,1+fv,i−j−1,1+wu,v},j=0maxi{fu,j,2+fv,i−j,0})
在转移结束后,执行:
f u , i , 0 : = max ( f u , i , 0 , f u , i − 1 , 1 , f u , i , 2 ) f_{u,i,0}:=\max(f_{u,i,0},f_{u,i-1,1},f_{u,i,2}) fu,i,0:=max(fu,i,0,fu,i−1,1,fu,i,2)
答案即为 f 1 , k + 1 , 0 f_{1,k+1,0} f1,k+1,0。
这个做法的时间复杂度是 O ( n k ) O(nk) O(nk) 而非 O ( n k 2 ) O(nk^2) O(nk2) 或 O ( n 2 ) O(n^2) O(n2) 的,期望得分 60 60 60 分。下面是来自我说说的简要证明:
- 每次合并,将第一维度从 0 0 0 枚举到了 m i n ( s i z [ u ] , k ) min(siz[u],k) min(siz[u],k),第二维度从 0 0 0 枚举到了 m i n ( s i z [ v ] , k ) min(siz[v],k) min(siz[v],k),那么其对复杂度的贡献就是它们的乘积。
- 我们可以认为,这里将 ⌈ \lceil ⌈ 目前已合并到 u u u 处的部分中 dfs \text{dfs} dfs 序前 k k k 大的 ⌋ \rfloor ⌋ 和 ⌈ \lceil ⌈ 以 v v v 为根的子树中 dfs \text{dfs} dfs 序前 k 小的 ⌋ \rfloor ⌋ 两两合并。
- 因此,从全局的角度来看,每个节点只会与 dfs \text{dfs} dfs 序与其差不超过 2 k 2k 2k 的点进行合并。
- 从而,总复杂度为 O ( 4 n k ) O(4nk) O(4nk)。
算法二
运气足够好可以发现, f 1 , 0 , f 1 , 1 , ⋯ , f 1 , k f_{1,0},f_{1,1},\cdots,f_{1,k} f1,0,f1,1,⋯,f1,k 组成了一个上凸函数。
于是,直接 wqs 二分就可以 O ( n log w ) O(n \log w) O(nlogw) 地通过本题了,尽管常数大得离谱。
这题就这么结束了?不,我们还需要注意一个细微的问题——凸函数上三点共线。
这会意味着什么呢?为什么会导致错误呢?考虑一段区间 [ l , r ] [l,r] [l,r],其中的点( f 1 , l , f 1 , l + 1 , ⋯ , f 1 , r f_{1,l},f_{1,l+1},\cdots,f_{1,r} f1,l,f1,l+1,⋯,f1,r)在一条直线上。那么,若 k ∈ [ l , r ] k \in [l,r] k∈[l,r],那么很容易出现切不到 k k k 的情况(即,虽然斜率正确,但是切到的点不对),导致 wqs 二分结束了都没有输出答案,从而导致 WA。
那么该如何处理呢?可以发现,在 wqs 二分结束后,若没有找出答案,那么我们依然可以得到该线的斜率及截距,其中前者即为二分结束后的下界 l l l,后者可以通过再跑一轮 dp \text{dp} dp 得出。于是,我们不难确定这个一次函数的表达式,就能简单地得出该一次函数在 k k k 处的值了。
综上所述,我们处理完了细节,本题被彻底解决。
Code
由于 dp \text{dp} dp 不仅要存储最大边权和,还要存储选出路径的条数,因此我采用了一个叫做 LCT 的结构体来维护,重载加法,小于之后十分方便,这里建议大家采用。
另外,请注意开 long long,并把二分上下界的绝对值设大。同时,二分上下界有人证明过可以设置为整数了,于是我就写了整数。虽然我觉得这是错的,所以大家还是用 double 吧。
#include <bits/stdc++.h>
#define int long long
#define chkmax(a,b) ((a)<(b)?(a)=(b):0)
using namespace std;
const int maxl=300005;int read(){int s=0,w=1;char ch=getchar();while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}return s*w;
}
int n,k,cnt,l,r;
int head[maxl];struct edge{int nxt,to,dis;}e[maxl<<1];struct LCT{int val,x;LCT(int xx=0,int yy=0):val(xx),x(yy){};friend bool operator<(LCT a,LCT b){return a.val==b.val?a.x>b.x:a.val<b.val;}friend LCT operator+(LCT a,LCT b){return LCT(a.val+b.val,a.x+b.x);}friend LCT operator+(LCT a,int b){return LCT(a.val+b,a.x);}
}f[maxl][3],s;void add_edge(int u,int v,int w){cnt++;e[cnt].to=v,e[cnt].dis=w,e[cnt].nxt=head[u],head[u]=cnt;
}void dfs(int u,int fath){f[u][0]=f[u][1]=f[u][2]=LCT();for (int i=head[u];i;i=e[i].nxt){int v=e[i].to;if (v==fath) continue;dfs(v,u);chkmax(f[u][2],max(f[u][2]+f[v][0],f[u][1]+f[v][1]+s+e[i].dis));chkmax(f[u][1],max(f[u][1]+f[v][0],f[u][0]+f[v][1]+e[i].dis));chkmax(f[u][0],f[u][0]+f[v][0]);}chkmax(f[u][0],max(f[u][1]+s,f[u][2]));
}signed main(){n=read(),k=read()+1;for (int i=1;i<n;i++){int u=read(),v=read(),w=read();add_edge(u,v,w),add_edge(v,u,w);}l=-3e11,r=3e11;while (l<=r){int mid=(l+r)>>1;s=LCT(-mid,1);dfs(1,0);if (f[1][0].x==k) return cout<<f[1][0].val+k*mid<<endl,0;else if (f[1][0].x>k) l=mid+1;else r=mid-1;}s=LCT(-l,1),dfs(1,0);cout<<f[1][0].val+k*l<<endl;return 0;
}
[八省联考 2018] 林克卡特树 题解相关推荐
- LuoguP4383 [八省联考2018]林克卡特树lct
LuoguP4383 [八省联考2018]林克卡特树lct https://www.luogu.org/problemnew/show/P4383 分析: 题意等价于选择\(K\)条点不相交的链,使得 ...
- luogu4383 bzoj5252[八省联考2018]林克卡特树lct
** [八省联考2018]林克卡特树lct** luogu bzoj 分析 很神仙的一道wqs二分.是真的不会切>-< 如果已经切完了,最优秀的方案就是每个联通块搞直径然后连起来一定是最优 ...
- P4383 [八省联考2018]林克卡特树(树形dp+wqs二分)
[八省联考2018]林克卡特树 题目大意:给定一棵有负权边的树,现在必须恰好删去 k k k条边,并加上恰好 k k k条权值为 0 0 0的边,要求最大化它的直径长度. 首先考虑删去 K K K条边 ...
- [八省联考2018]林克卡特树
林克卡特树 题解 挺简单的一道题. 原题断 k k k条边连 k k k条边权为 0 0 0的边相当于寻去 k + 1 k+1 k+1条不相交链出来,将它们连上得到的结果. 所以我们要从原树中选取 k ...
- 洛谷P4383 [八省联考2018]林克卡特树lct(DP凸优化/wqs二分)
题目描述 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做"LC ...
- 洛谷.4383.[八省联考2018]林克卡特树lct(树形DP 带权二分)
题目链接 \(Description\) 给定一棵边带权的树.求删掉K条边.再连上K条权为0的边后,新树的最大直径. \(n,K\leq3\times10^5\). \(Solution\) 题目可以 ...
- P4383 [八省联考 2018] 林克卡特树(wqs二分、树形dp)
解析 它还真的不难. 乐. 这题没做出来有些谔谔. 外层wqs二分显而易见,里面不知道为啥我总觉得这个题可以贪心. 然后一直试图在原树直径上下功夫,一筹莫展. 看到题解"dp"两个 ...
- P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分
$ \color{#0066ff}{ 题目描述 }$ 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的 ...
- [八省联考2018]林克卡特树lct
题面在这里 description 一个\(N\)个点的\(Tree\),每条边有一个整数边权\(v_i\),表示走这条边会获得\(v_i\)的收益: 小\(L\)需要控制主角\(Link\),\(C ...
最新文章
- 排列(permutation)2_6
- maven pom java版本_Maven更新POM中的JDK版本(比如更新为JDK1.8)
- 浩鲸科技携手阿里云原生共同打造“场域运营数字化解决方案”
- [leetcode]Binary Tree Inorder Traversal
- 《FusionCharts学习及使用笔记》之 第一篇
- 需要规范日志格式_Node开发的日志规范
- Gstreamer 搭建RTSP服务器(九)
- JavaScript 启动性能瓶颈分析与解决方案
- Uboot下SPI FLASH的添加(SPI 控制器采用软件模拟的方式)
- the7主题中文版升级到v.6.7.1(2018年7月27日)
- ajax的三种传参方式
- html中的项目符号和编号,CSS重新定义项目符号和编号
- 服务器上搭建Lepus——开源的数据库监控系统
- 90% 都会的 ES6 简化代码技巧,你用过哪些?
- python抢票开发——设备预约助手实现
- samba服务器介绍
- 【UML】——活动图
- gr java ch,Apache POI 快速指南.pdf
- Jquery 添加删除属性、添加删除class、添加删除Css
- 矩形波发生器c语言,矩形波发生器电路设计方案汇总(六款模拟电路设计原理图详解)...