#树形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.但暴力的极限也就到此为止. 每次 ...
随机推荐
- 【Android 逆向】【ARM汇编】 全局资源重定位
资源重定位解释: 字符串反汇编代码解释: .rodata:00001E20 __exidx_end DCB "a + b = %d" ; DATA_XREF: main+28^o ...
- Ubuntu下docker部署
使用docker进行容器化集成部署 远程服务器更新源 更新ubuntu的apt源 sudo apt-get update 安装包允许apt通过HTTPS使用仓库 sudo dpkg --configu ...
- 【Azure 事件中心】Spring Boot 集成 Event Hub(azure-spring-cloud-stream-binder-eventhubs)指定Partition Key有异常消息
问题描述 在Spring Boot应用中集成Event Hub,发送消息时指定Partition Key,日志中发现异常: 应用使用Event Hub版本为 <dependency> &l ...
- CYQ.Data 支持 DaMeng 达梦数据库
DaMeng 达梦数据库介绍: 达梦数据库(DMDB)是中国自主研发的关系型数据库管理系统,由达梦科技股份有限公司开发. 达梦数据库提供了企业级的数据库解决方案,广泛应用于金融.电信.政府.制造等行业 ...
- 使用go语言开发自动化API测试工具
前言 上一篇文章说到我还开发了一个独立的自动测试工具,可以根据 OpenAPI 的文档来测试,并且在测试完成后输出测试报告,报告内容包括每个接口是否测试通过和响应时间等. 这个工具我使用了 go 语言 ...
- AOP+自定义注解实现权限校验-2022新项目
一.业务场景 当前本人参与开发的是一个业务中台系统,所谓的中台简单的理解就是把相同的功能给抽取出来.比如系统A.B.C.D都需要进行用户登录操作,那么可以把用户信息管理这一块抽取出来为一个独立的系统E ...
- 真实开发中-redis在项目中的应用场景
一.需求描述 从自己当前负责参与开发的一个项目中来看,redis主要的应用场景有如下几个,第一个是保存用户信息,这个需要频繁的获取.比如 在打开某一个页面进行查询时,就先需要获取用户信息,看用户是否具 ...
- git clone error: RPC failed; curl 18 transfer closed with outstanding read data remaining
备忘 git clone比较大的工程时,出现这种错误:error: RPC failed; curl 18 transfer closed with outstanding read data rem ...
- RTMP、X264与交叉编译
RTMP.X264与交叉编译 与HTTP(超文本传输协议)同样是一个基于TCP的Real Time Messaging Protocol(实时消息传输协议).由Adobe Systems公司为Fl ...
- 安装centos系统必做操作
关闭SELinux sed -i '/^SELINUX=/c SELINUX=disabled' /etc/selinux/config 在Linux系统中临时关闭SELinux(Security-E ...