P4383 [八省联考 2018] 林克卡特树
P4383 [八省联考 2018] 林克卡特树
米奇妙妙题
题目的主要操作就是断掉一条边再连一条边权为\(0\)的边
我们考虑先不连那些后来加上的边权为\(0\)的边,先把所有的需要断的边都断掉,那么就形成了\(k+1\)个连通块
接下来的任务就是把所有的连通块连接在一起,可以发现,使得答案最大的连接法就是找出每个连通块中的直径后将所有块的直径依次相连,最后的答案就是所有块的直径的长度之和
现在我们就完成了问题的转化,那么现在我们要求的就是把原树分成\(k+1\)个连通块,使得所有块的直径之和最大
那么设\(f(k)\)表示将树分成\(k+1\)块的答案,设\(k=p\)时答案最大,那么显然有当\(k\)增大或减小时\(f(k)\)都会减小或不变
形式化的表达就是\(f(x+1)-f(x)\leq f(x)-f(x-1)\),也就是说对于相邻两点的斜率有\(x\)和\(x-1\)的斜率大于\(x\)和\(x+1\)的斜率
证明:
对于第一种表示方法,可以理解为从\(f(p)\)的方案一步步改成了\(f(k)\),那么显然改的边越多,答案越劣
对于第二种表示方法,可以理解为从\(f(x-1)\)的方案改成\(f(x)\),再从\(f(x)\)改成\(f(x+1)\),既然\(f\)表示的是最优解,那么肯定从\(f(x-1)\)改成\(f(x)\)时会选择最优的方案,从\(f(x)\)改成\(f(x+1)\)只能选剩下的方案中最优的方案,也就是说肯定不会优于从\(f(x-1)\)改成\(f(x)\)
那么我们就可以使用\(wqs\)二分,剩下的就是如何找在当前二分的\(mid\)斜率下的最大切点,也即最大的连通块数
因为我们计算的是每个连通块里的直径,也就是说对于这条直径中的点,它们的度数(指与直径中有多少个点相连)只有\(1\)和\(2\)两种情况,而不是直径中的点,其度数只能是\(0\),但是因为我们在转移时肯定需要将点分成待与父亲相连的点和不会再与父亲相连的点,所以稍微整理下这两种东西,我们就可以得出\(dp\)状态了:
\(dp[x][0]\)表示\(x\)不会再相连,当前\(x\)子树内的答案,此时\(x\)的度数为\(0/1/2\),并且此时的\(x\)有两种情况,一种是其是直径中的点,一种是其不是直径中的点
\(dp[x][1]\)表示\(x\)还可以再相连,当前\(x\)子树内的答案,此时\(x\)的度数\(\leq1\),且\(x\)是直径中的点
\(dp[x][2]\)表示\(x\)不会再相连,但是\(x\)会和其两个儿子相连,也就是说\(x\)是其所处连通块中那条^状的直径的那个转折点,事实上\(dp[x][2]\)是属于\(dp[x][0]\)的,但是为了方便转移,单独提出来,且\(x\)是直径中的点
要注意的是因为我们现在在使用\(wqs\)二分,用\(mid\)表示当前二分的斜率,则有\(y=f(x_0)-k\times x_0\),也就是说我们每分出一个连通块答案就需要\(-k\)
那么转移有:
for(int i=1;i<=n;++i) dp[i][0]=dp[i][1]={0,0},dp[i][2]={-mid,1};//初始化
for(int i=head[x],y;i;i=tree[i].nxt) if((y=tree[i].v)!=fa){
dfs(y,x);
dp[x][2]=max(dp[x][2]+dp[y][0],dp[x][1]+dp[y][1]+make_pair(tree[i].val-mid,1)),
dp[x][1]=max(dp[x][1]+dp[y][0],dp[x][0]+dp[y][1]+make_pair(1ll*tree[i].val,0)),
dp[x][0]=dp[x][0]+dp[y][0];
}
dp[x][0]=max(dp[x][0],max(dp[x][1]+make_pair(-mid,1),dp[x][2]));
\(dp\)是\(pair\)类型,\(first\)是答案,\(second\)是满足当前答案的最大的连通块数量
这里就可以比较直观的感受定义了
因为\(dp[x][0]\)还包含了\(x\)不是直径中的点的情况,所以\(dp[i][0]\)的初始化是\(\{0,0\}\)而不是和\(dp[i][2]\)一样的\(\{-mid,1\}\),包括大括号中转移部分,与\(dp[x][0]\)有关的部分转移不用像\(dp[i][2]\)一样要\(-mid\)就是这个原因
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+5;
const ll INF=1e18;
int n,k;
int head[N],cntt;
struct node{
int nxt,v,val;
}tree[N<<1];
void add(int u,int v,int val){
tree[++cntt]={head[u],v,val},head[u]=cntt;
tree[++cntt]={head[v],u,val},head[v]=cntt;
}
ll l,r,mid;
pair<ll,int> dp[N][3];
pair<ll,int> operator + (pair<ll,int> a,pair<ll,int> b){ return {a.first+b.first,a.second+b.second}; }
void dfs(int x,int fa){
for(int i=head[x],y;i;i=tree[i].nxt) if((y=tree[i].v)!=fa){
dfs(y,x);
dp[x][2]=max(dp[x][2]+dp[y][0],dp[x][1]+dp[y][1]+make_pair(tree[i].val-mid,1)),dp[x][1]=max(dp[x][1]+dp[y][0],dp[x][0]+dp[y][1]+make_pair(1ll*tree[i].val,0)),dp[x][0]=dp[x][0]+dp[y][0];
}
dp[x][0]=max(dp[x][0],max(dp[x][1]+make_pair(-mid,1),dp[x][2]));
}
int get(){
for(int i=1;i<=n;++i) dp[i][0]=dp[i][1]={0,0},dp[i][2]={-mid,1};
dfs(1,0);
return max(dp[1][0],max(dp[1][1],dp[1][2])).second;
}
int main(){
scanf("%d%d",&n,&k),++k;
for(int i=1,u,v,w;i<n;++i) scanf("%d%d%d",&u,&v,&w),add(u,v,w),r+=abs(w);
l=-r;
while(l<r){
mid=l+r+1>>1;
if(get()>=k) l=mid;
else r=mid-1;
}
mid=l,get(),printf("%lld",l*k+dp[1][0].first);
return 0;
}
P4383 [八省联考 2018] 林克卡特树的更多相关文章
- P4383 [八省联考2018]林克卡特树 树形dp Wqs二分
LINK:林克卡特树 作为树形dp 这道题已经属于不容易的级别了. 套上了Wqs二分 (反而更简单了 大雾 容易想到还是对树进行联通情况的dp 然后最后结果总和为各个联通块内的直径. \(f_{i,j ...
- 洛谷P4383 [八省联考2018]林克卡特树lct(DP凸优化/wqs二分)
题目描述 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做“LCT” 的挑 ...
- P4383 [八省联考2018]林克卡特树lct
题目链接 题意分析 一句话题意就是 : 让你选出\((k+1)\)条不相交的链 使得这些链的边权总和最大 (这些链可以是点) 我们考虑使用树形\(DP\) \(dp[i][j][0/1/2]\)表示以 ...
- P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分
$ \color{#0066ff}{ 题目描述 }$ 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的 ...
- LuoguP4383 [八省联考2018]林克卡特树lct
LuoguP4383 [八省联考2018]林克卡特树lct https://www.luogu.org/problemnew/show/P4383 分析: 题意等价于选择\(K\)条点不相交的链,使得 ...
- [八省联考2018]林克卡特树lct——WQS二分
[八省联考2018]林克卡特树lct 一看这种题就不是lct... 除了直径好拿分,别的都难做. 所以必须转化 突破口在于:连“0”边 对于k=0,我们求直径 k=1,对于(p,q)一定是从p出发,走 ...
- luoguP4383 [八省联考2018]林克卡特树(树上dp,wqs二分)
luoguP4383 [八省联考2018]林克卡特树(树上dp,wqs二分) Luogu 题解时间 $ k $ 条边权为 $ 0 $ 的边. 是的,边权为零. 转化成选正好 $ k+1 $ 条链. $ ...
- 洛谷 4383 [八省联考2018]林克卡特树lct——树形DP+带权二分
题目:https://www.luogu.org/problemnew/show/P4383 关于带权二分:https://www.cnblogs.com/flashhu/p/9480669.html ...
- [八省联考2018]林克卡特树lct
题解: zhcs的那个题基本上就是抄这个题的,不过背包的分数变成了70分.. 不过得分开来写..因为两个数组不能同时满足 背包的话就是 $f[i][j][0/1]$表示考虑i子树,取j条链,能不能向上 ...
- BZOJ5252 八省联考2018林克卡特树(动态规划+wqs二分)
假设已经linkcut完了树,答案显然是树的直径.那么考虑这条直径在原树中是怎样的.容易想到其是由原树中恰好k+1条点不相交的链(包括单个点)拼接而成的.因为这样的链显然可以通过linkcut拼接起来 ...
随机推荐
- apisix~key-auth多消费的使用
在 APISIX 中使用 key-auth 插件实现基于密钥的认证,以下是详细的配置步骤,包括如何保存密钥和证书,以及如何将这些信息分配给客户端 A 和 B. 场景说明 服务 C 是后端服务,需要通过 ...
- 请运行命令来卸载Oracle主目录
卸载Oracle数据库碰见的一个不一样的操作,留爪. 正常点的软件卸载直接点卸载即可, Oracle 卸载非要来点不一样警告, 如图: 注意:不要被图里的斜杠符号忽略了,准确的应该是: # 注意斜杠 ...
- nrm
nrm npm install -g nrm nrm ls nrm use taobao Tips:不要使用cnpm,会有些奇怪的问题,导致npm install失败. 参考
- Tengine-rpm 基于Tengine 3.1深度定制优化
Tengine RPM Tengine是亚洲最大的电子商务网站淘宝网推出的高性能的HTTP和反向代理web服务器.它基于 Nginx HTTP 服务器,拥有许多高级功能.事实证明,Tengine 在淘 ...
- Linux 关机与重启命令
关机命令 我们可以使用以下三种命令来关机 Linux : 1.立刻关机(需要root用户) shutdown -h now 10 分钟后自动关机 shutdown -h 10 2.立刻关机 halt ...
- java基础之线程池
一.线程池:提前创建多个线程存放到集合容器中,其中的线程可以反复使用,减少资源的开销 作用就是:线程执行完一个任务,并不被销毁,而是可以继续执行其他的任务 使用线程池中线程对象的步骤: 1. 创建线程 ...
- Java 中的字符串常量池和运行时常量池
Java 中的字符串常量池和运行时常量池 1. 字符串常量池(String Constant Pool) 定义 字符串常量池是 JVM 内存中专门用于存储字符串字面量和通过 intern() 方法加入 ...
- windows下redis设置redis开机自启动
windows系统下启动redis命令 进入redis安装目录 cd redis 输入 redis-server.exe redis.windows.conf 启动redis命令,看是否成功 可能会启 ...
- AI Agent离我们有多远?认知革命的开始(上篇)
认知是成本最低的对冲. --张三思维进化论 深夜3点,我与AI Agent的惊人对话 2025年的一个深夜,我习惯性地打开电脑处理一些工作.身为一个从大厂转型的自由职业者,夜晚往往是我效率最高的时段. ...
- 记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑
开心一刻 刚毕业的侄子给我发消息侄子:叔,人生太难了我:怎么呢?侄子:工作太难了,感情也太难了,怎么什么都这么难我:你还小啊侄子:大了就不难了?我:大了你就习惯了 问题复现 先准备表:数据源( tbl ...