简化版描述:

给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。
其中链长度定义为链上点的个数。
 

有几个不同的做法:

1.sort+并查集+树的直径。边从大到小加入,并查集维护连通块,记录连通块的直径的两个端点,合并连通块的时候更新直径,并且用len*bian[i].w更新答案

  有排序,O(nlogn)

2.点分治+树状数组。点分治路径合并的时候挺恶心。先都扫一遍所有子树,把路径最小值作为下标,链长作为权值放进树状数组里。

  再枚举子树搜一遍,先减去当前子树的贡献,再搜的时候从树状数组找后缀最大值。思想就是钦定最小值在当前子树的某个路径中

  O(nlog^2)

3.点分治

  把过重心的子树分成两堆递归?不懂。。。O(nlogn)

  代码太长,还不如写边分治

4.边分治

https://blog.csdn.net/litble/article/details/80853633

 本身其实点分治还可以暴力枚举重心子树的所有兄弟然后双指针,从而不用树状数组,但是复杂度是O(度数^2nlogn)的。不优秀

边分治就两个儿子自然好办啦

三度化然后边分治,两个子树的路径存进两个数组,按照最小值sort,然后倒序枚举双指针。记录最小值不小于当前钦定子树的最大的深度,左右各做一遍即可

过虚点其实一定过了x,所以虚点权值定为x的权值。

虚边的权值就是0,实边是1,两点链长是深度+1

代码:

注意,如果对面的儿子没有选择一个点,那么不能计算当前的中心边的权值

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define mk(x,y) make_pair(x,y)
#define fi first
#define se second
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
const int inf=0x3f3f3f3f;
int n;
int tot;
int val[N];
struct node{
int nxt,to;
int w;
}e[*N],bian[*N];
int cnt1=,cnt2;
int hd[N],pre[N];
ll ans;
void add(int x,int y,int z){
e[++cnt1].nxt=hd[x];
e[cnt1].to=y;
e[cnt1].w=z;
hd[x]=cnt1;
}
void add_c(int x,int y){
bian[++cnt2].nxt=pre[x];
bian[cnt2].to=y;
pre[x]=cnt2;
}
void rebuild(int x,int fa){
int ff=;
for(reg i=pre[x];i;i=bian[i].nxt){
int y=bian[i].to;
if(y==fa) continue;
if(!ff){
add(x,y,);
add(y,x,);
ff=x;
}else{
int tmp=++tot;
val[tmp]=val[x];
add(ff,tmp,);add(tmp,ff,);
add(tmp,y,);add(y,tmp,);
ff=tmp;
}
rebuild(y,x);
}
} pair<int,int>ls[N],rs[N];
int lsc,rsc;
bool cmp(pair<int,int>A,pair<int,int>B){
if(A.fi==B.fi) return A.se<B.se;
return A.fi<B.fi;
}
int totsz;
int rt1,rt2,edge;
bool vis[*N];
int sz[N],mx;
void dfs1(int x,int fa){
sz[x]=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
if(vis[i]) continue;
dfs1(y,x);
int now=max(sz[y],totsz-sz[y]);
if(now<mx) {
mx=now;rt1=x;rt2=y;edge=i;
}
sz[x]+=sz[y];
}
}
void dfs2(int x,int fa,int mi,int dep,int typ){
if(typ==){
ls[++lsc]=mk(mi,dep);
}else{
rs[++rsc]=mk(mi,dep);
}
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa||vis[i]) continue;
dfs2(y,x,min(mi,val[y]),dep+e[i].w,typ);
}
}
void divi(int x,int from ){
//cout<<" divi "<<x<<" "<<totsz<<" from "<<from<<endl;
if(totsz==) return; rt1=rt2=edge=;
mx=inf;
dfs1(x,);
//cout<<" rt1 "<<rt1<<" rt2 "<<rt2<<" sz "<<sz[rt1]<<" "<<sz[rt2]<<endl;
vis[edge]=vis[edge^]=;
lsc=rsc=;
dfs2(rt1,,val[rt1],,);
dfs2(rt2,,val[rt2],,);
sort(ls+,ls+lsc+,cmp);
sort(rs+,rs+rsc+,cmp);
int mxdep=;
int ptr=rsc;
for(reg i=lsc;i>=;--i){
while(ptr&&rs[ptr].fi>=ls[i].fi){
mxdep=max(mxdep,rs[ptr].se);--ptr;
}
ans=max(ans,(ll)((ll)mxdep+e[edge].w+ls[i].se+)*ls[i].fi);
}
mxdep=;ptr=lsc;
for(reg i=rsc;i>=;--i){
while(ptr&&ls[ptr].fi>=rs[i].fi){
mxdep=max(mxdep,ls[ptr].se);--ptr;
}
ans=max(ans,(ll)((ll)mxdep+e[edge].w+rs[i].se+)*rs[i].fi);
}
int szrt1=totsz-sz[rt2];
int szrt2=sz[rt2];
int tmprt1=rt1,tmprt2=rt2;
totsz=szrt1;
divi(tmprt1,x);
totsz=szrt2;
divi(tmprt2,x);
}
int main(){
rd(n);
for(reg i=;i<=n;++i) rd(val[i]),ans=max(ans,(ll)val[i]);
int x,y;
for(reg i=;i<n;++i){
rd(x);rd(y);
add_c(x,y);add_c(y,x);
}
tot=n;
rebuild(,);
totsz=tot;
//cout<<" tot "<<tot<<endl;
divi(,);
printf("%lld",ans);
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/2/25 9:29:02
*/

bzoj2870最长道路tree——边分治的更多相关文章

  1. 【BZOJ2870】最长道路tree 点分治+树状数组

    [BZOJ2870]最长道路tree Description H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样.每个路口都有很多车辆来 ...

  2. BZOJ2870—最长道路tree

    最长道路tree Description H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样.每个路口都有很多车辆来往,所以每个路口i都 ...

  3. [BZOJ2870]最长道路tree:点分治

    算法一:点分治+线段树 分析 说是线段树,但是其实要写树状数组卡常. 代码 #include <bits/stdc++.h> #define rin(i,a,b) for(register ...

  4. BZOJ2870: 最长道路tree

    题解: 子树分治的做法可以戳这里:http://blog.csdn.net/iamzky/article/details/41120733 可是码量... 这里介绍另一种好写又快的方法. 我们还是一颗 ...

  5. bzoj 2870 最长道路tree——边分治

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2870 关于边分治:https://www.cnblogs.com/Khada-Jhin/p/ ...

  6. BZOJ2870 最长道路tree(并查集+LCA)

    题意 (n<=50000) 题解 #include<iostream> #include<cstring> #include<cstdio> #include ...

  7. 【BZOJ2870】最长道路(边分治)

    [BZOJ2870]最长道路(边分治) 题面 BZOJ权限题 Description H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样 ...

  8. 【bzoj2870】最长道路tree 树的直径+并查集

    题目描述 给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大. 其中链长度定义为链上点的个数. 输入 第一行N 第二行N个数分别表示1~N的点权v[i] 接下来N-1行每 ...

  9. BZOJ2870 最长道路

    题意:给定树,有点权.求一条路径使得最小点权 * 总点数最大.只需输出这个最大值.5w. 解:树上路径问题,点分治. 考虑合并两个子树的时候,答案的形式是val1 * (d1 + d2),当1是新插入 ...

随机推荐

  1. SpringBoot日记——Spring的安全配置-登录认证与授权

    安全是每个项目开发中都需要考虑的,比如权限控制,安全认证,防止漏洞攻击等. 比较常见的安全框架有:Apache的shiro.Spring Security等等,相信用shiro的用户群体更多,而sec ...

  2. Mvc_缓存浅谈

    缓存是将信息放在内存中以避免频繁访问数据库从数据库中提取数据,在系统优化过程中,缓存是比较普遍的优化做法和见效比较快的做法. 对于MVC有Control缓存和Action缓存. 一.Control缓存 ...

  3. C#抽象类跟接口

    抽象类描述的是一个什么东西,属性. 抽象类是对类的抽象,描述是什么  抽象类,继承后重写接口描述的是他做什么,行为.接口是对行为的抽象,描述做什么  ,进行继承后实行接口

  4. 第三周作业————————word count

    #include <stdio.h> void main() { FILE *fp; , str, word, pu, ch; int g; str = ; word = ; pu = ; ...

  5. 个人阅读作业WEEK7 (软件工程的瀑布, 大泥球, 教堂,集市,和银弹)

    一 . 关于银弹 (Silver Bullet) 银弹,被引申为解决问题的有效办法.IBM大型机之父福瑞德·布鲁克斯在1986年的论文<没有银弹>中表达了他的观点:软件工程中不存在银弹—— ...

  6. 个人作业 - Week3 - 案例分析

    调研与评测 真实用户采访: 用户姓名: 刘斯盾 用户的背景和需求: 用户是一位计算机专业学生,需要浏览技术博客来扩充自己的学识. 用户使用博客园证明: 产品是否解决用户问题: 在码代码过程中遇到的很多 ...

  7. CRM 数据查重

    2.8 小工具 · 纷享销客产品手册https://www.fxiaoke.com/mob/guide/crmdoc/src/2-8%E5%B0%8F%E5%B7%A5%E5%85%B7.html C ...

  8. From 简书 转帖一下如何安装k8s1.10 改天做下实验. https://www.jianshu.com/p/9c7e1c957752

    centos7.3 kubernetes/k8s 1.10 离线安装 老菜_misa 关注 2018.04.25 23:57 字数 1243 阅读 266评论 1喜欢 3 本文介绍在centos7.3 ...

  9. springsession 实现session 共享

    首先加入依赖1 <dependency> <groupId>org.springframework.session</groupId> <artifactId ...

  10. 堆排序获取TopN

    package com.zjl.tool.sort; /** * 求前面的最大K个 解决方案:小根堆 (数据量比较大(特别是大到内存不可以容纳)时,偏向于采用堆) * @author 张恩备 * @d ...