#树形dp,直径#51nod 1812 树的双直径
题目
给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),
使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有1个点则链长视为0)。
\(n\leq 4*10^5,|c_i|\leq 10^9\)
分析
首先考虑一条直径就是维护最大值和次大值,
然后换根就可以得到经过任意一点的直径 \(f[x]\)。
但是两条直径还要考虑不相交,那么分类讨论一下。
如果直径处在两个不同的子树内,可以提前处理 \(dp[x]\) 表示 \(x\) 的子树内 \(f[x]\) 的最大值。
对于 \(x\),求出 \(\max\{dp[y]*dp[y']\}\) 即可
否则,直径一定可以被表示成一条经过 \(x\) 且不经过 \(y\),一条在以 \(y\) 为根的子树内,也就是 \(dp[y]\)
\(x\) 这部分挺难求的,任意一点直径却并没有保证它不经过 \(y\),所以还要维护第三大值,剔除掉 \(y\) 的贡献,再贡献给 \(y\)
最后如果要卡常,就只在答案这里开 __int128 就可以了。
心路历程:提交了16次,一开始根本没发现可以处在两个不同的子树内,而且对求第三大值犹豫再三。
而且调题真的很难受,没求处在两个不同的子树内的时候,只有一个点是RE的(玄学,令根为 \(\left\lceil\frac{n}{2}\right\rceil\) 就会少点RE)
但是其实调题的时候做法还不够完整,因为这么做,我第五个点是WA的,接着就一直查错,然后发现了这种情况,一开始想复杂了,后来利用 \(\max\{dp[y]*dp[y']\}\) 就可以了
代码
#include <cstdio>
#include <cctype>
using namespace std;
const int N=400011; typedef long long lll;
struct node{int y,w,next;}e[N<<1]; __int128 ans;
lll Mn[N],Mx[N],mn[N][3],mx[N][3]; int et=1,as[N],n;
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
void print(__int128 ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void Max(lll &a,lll b){a=a>b?a:b;}
void Min(lll &a,lll b){a=a<b?a:b;}
__int128 max(__int128 a,__int128 b){return a>b?a:b;}
void dfs1(int x,int fa){
for (int i=as[x],flag;i;i=e[i].next)
if (e[i].y!=fa){
dfs1(e[i].y,x);
flag=1;
for (int k=0;k<3&&flag;++k)
for (int j=k;j<3&&flag;++j)
if (mn[x][j]>mn[e[i].y][k]+e[i].w){
for (int _j=2;_j>j;--_j) mn[x][_j]=mn[x][_j-1];
mn[x][j]=mn[e[i].y][k]+e[i].w,flag=0;
}
flag=1;
for (int k=0;k<3&&flag;++k)
for (int j=k;j<3&&flag;++j)
if (mx[x][j]<mx[e[i].y][k]+e[i].w){
for (int _j=2;_j>j;--_j) mx[x][_j]=mx[x][_j-1];
mx[x][j]=mx[e[i].y][k]+e[i].w,flag=0;
}
ans=max(ans,(__int128)Mn[x]*Mn[e[i].y]);
ans=max(ans,(__int128)Mx[x]*Mx[e[i].y]);
Min(Mn[x],Mn[e[i].y]),Max(Mx[x],Mx[e[i].y]);
}
Min(Mn[x],mn[x][0]+mn[x][1]);
Max(Mx[x],mx[x][0]+mx[x][1]);
}
void dfs2(int x,int fa){
lll MN[3],MX[3];
for (int i=0;i<3;++i) MN[i]=mn[x][i],MX[i]=mx[x][i];
for (int i=as[x],flag;i;i=e[i].next)
if (e[i].y!=fa){
for (int j=0;j<3;++j) mn[x][j]=MN[j],mx[x][j]=MX[j];
flag=1;
for (int k=0;k<3&&flag;++k)
for (int j=k;j<3&&flag;++j)
if (mn[x][j]==mn[e[i].y][k]+e[i].w){
for (int _j=j;_j<2;++_j) mn[x][_j]=mn[x][_j+1];
mn[x][2]=flag=0;
}
flag=1;
for (int k=0;k<3&&flag;++k)
for (int j=k;j<3&&flag;++j)
if (mx[x][j]==mx[e[i].y][k]+e[i].w){
for (int _j=j;_j<2;++_j) mx[x][_j]=mx[x][_j+1];
mx[x][2]=flag=0;
}
flag=1;
for (int k=0;k<3&&flag;++k)
for (int j=k;j<3&&flag;++j)
if (mn[e[i].y][j]>mn[x][k]+e[i].w){
for (int _j=2;_j>j;--_j) mn[e[i].y][_j]=mn[e[i].y][_j-1];
mn[e[i].y][j]=mn[x][k]+e[i].w,flag=0;
}
flag=1;
for (int k=0;k<3&&flag;++k)
for (int j=k;j<3&&flag;++j)
if (mx[e[i].y][j]<mx[x][k]+e[i].w){
for (int _j=2;_j>j;--_j) mx[e[i].y][_j]=mx[e[i].y][_j-1];
mx[e[i].y][j]=mx[x][k]+e[i].w,flag=0;
}
ans=max(ans,(__int128)Mn[e[i].y]*(mn[x][0]+mn[x][1]));
ans=max(ans,(__int128)Mx[e[i].y]*(mx[x][0]+mx[x][1]));
dfs2(e[i].y,x);
}
}
int main(){
n=iut();
for (int i=1;i<n;++i){
int x=iut(),y=iut(),w=iut();
e[++et]=(node){y,w,as[x]},as[x]=et;
e[++et]=(node){x,w,as[y]},as[y]=et;
}
dfs1((n+1)>>1,0),dfs2((n+1)>>1,0);
print(ans);
return 0;
}
#树形dp,直径#51nod 1812 树的双直径的更多相关文章
- 51nod 1812 树的双直径 题解【树形DP】【贪心】
老了-稍微麻烦一点的树形DP都想不到了. 题目描述 给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有 ...
- 51nod1812树的双直径(换根树DP)
传送门:http://www.51nod.com/Challenge/Problem.html#!#problemId=1812 题解:头一次写换根树DP. 求两条不相交的直径乘积最大,所以可以这样考 ...
- LOJ2250 [ZJOI2017] 仙人掌【树形DP】【DFS树】
题目分析: 不难注意到仙人掌边可以删掉.在森林中考虑树形DP. 题目中说边不能重复,但我们可以在结束后没覆盖的边覆盖一个重复边,不改变方案数. 接着将所有的边接到当前点,然后每两个方案可以任意拼接.然 ...
- hdu6035 Colorful Tree 树形dp 给定一棵树,每个节点有一个颜色值。定义每条路径的值为经过的节点的不同颜色数。求所有路径的值和。
/** 题目:hdu6035 Colorful Tree 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题意:给定一棵树,每个节点有一个颜色值.定 ...
- 51nod"省选"模测 A 树的双直径(树形dp)
题意 题目链接 Sol 比赛结束后才调出来..不多说啥了,就是因为自己菜. 裸的up-down dp,维护一下一个点上下的直径就行,一开始还想了个假的思路写了半天.. 转移都在代码注释里 毒瘤题目卡空 ...
- hdu 2196(方法1:经典树形DP+方法2:树的直径)
Computer Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- 【洛谷】2607: [ZJOI2008]骑士【树形DP】【基环树】
P2607 [ZJOI2008]骑士 题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的Y国发动了一 ...
- HDU4612(Warm up)2013多校2-图的边双连通问题(Tarjan算法+树形DP)
/** 题目大意: 给你一个无向连通图,问加上一条边后得到的图的最少的割边数; 算法思想: 图的边双连通Tarjan算法+树形DP; 即通过Tarjan算法对边双连通缩图,构成一棵树,然后用树形DP求 ...
- 【BZOJ-2286】消耗战 虚树 + 树形DP
2286: [Sdoi2011消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2120 Solved: 752[Submit][Status] ...
- BZOJ5287 HNOI2018毒瘤(虚树+树形dp)
显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp.考场上写的是最暴力的O(3n-mn),成功比大众分少10分.容斥或者注意到某些枚举是不必要的就能让底数变成2.但暴力的极限也就到此为止. 每次 ...
随机推荐
- golang常用库包:redis操作库go-redis使用(01)-Redis数据类型简介和连接Redis的几种方式
第一篇:go-redis使用,介绍Redis基本数据结构和其他特性,以及 go-redis 连接到Redis(本篇) https://www.cnblogs.com/jiujuan/p/1720716 ...
- 【Android逆向】frida 破解 滚动的天空
1. apk 安装到手机中 2. 玩十次之后,会提示 充值 3. adb shell dumpsys window | grep mCurrentFocus 查看一些当前activity是哪一个 是 ...
- 双层循环练习,pass_break_continue,和for循环---day06
1.双层循环练习 2.pass_break_continue pass:在代码块中无代码可写时,可用pass占位 break:终止当前循环,只能应用在循环里 continue:跳过当前循环,从下一次开 ...
- React 组件通信方式
人生的游戏不在于拿了一副好牌,而在于怎样去打好坏牌,世上没有常胜将军,勇于超越自我者才能得到最后的奖杯. 1. 父子组件通信方式 1.1 父组件传递到子组件 直接通过属性进行传递,数据的传递可以提高组 ...
- 【算法day2】复杂度和简单排序算法(2)
插入排序 有以下数组 数组:[2,4,3,6,1] 序号:[0,1,2,3,4] 第一次排序(范围0~0):2左边没东西,不动 第二次排序(范围0~1):4左边是2,4大不动 第三次排序(范围0~2) ...
- 开源的 Sora 复现方案,成本降低近一半!
近日,开发 ChatGPT 的 OpenAI 公司又放出王炸 Sora,一个可以根据文本生成视频的 AI 模型. 上图就是 OpenAI 公布的 Sora 生成的视频片段,可以毫不夸张地说 Sora ...
- Java核心之细说泛型
泛型是什么? 等你使用java逐渐深入以后会了解或逐步使用到Java泛型.Java 中的泛型是 JDK 5 中引入的功能之一."Java 泛型 "是一个技术术语,表示一组与定义和使 ...
- 如何使用疯狂URL获取抖音推流码地址(抖音推流码地址获取教程)
本节所用到的工具:疯狂URL.OBS推流工具 什么是推流地址? 平时我们如果是下载直播,叫拉流.但如果是你自己要直播,属于上传直播流数据,叫推流,即:把直播流数据推送到视频服务器,然后别人才能看到直播 ...
- 基于RocketMQ实现分布式事务(半消息事务)
一.背景 RocketMQ的分布式事务可以称为"半消息事务". 二.原理 2.1原理 RocketMQ是靠半消息机制实现分布式事务: 事务消息:MQ 提供类似 X/Open XA ...
- [MAUI 项目实战] 音乐播放器(二):播放内核
播放控制服务 IMusicControlService: 播放控制类,用于当前平台播放器对象的操作,对当前所播放曲目的暂停/播放,下一首/上一首,快进快退(寻迹),随机.单曲模式等功能的控制. 播放控 ...