题目传送门

题目大意

给出一个\(n\)个点的树,每个点有点权,定义一条链的贡献为该链的点数乘上链上的权值和,求出树上所有链中的权值最大值。

\(n\le 5\times 10^4\)

思路

算是我入边分治的门的一道题吧。。。借鉴了\(\texttt{Miracle}\)巨佬的图、代码以及思路(那不就是转载么???(大雾

边分治的大概意思就是说,对于给出的一棵树,我们重新构造成一颗3度树,并且保证我们要求答案的所需性质并不会丢失。然后有一个性质:

于是我们找到重心边分治一下,递归次数就是\(\log n\)级别。于是就能在一个较优的时间复杂度内解决这个问题。

那我们如何对于原树建出一个保留所需信息的3度树呢?有两种方法,第一种就是这样:

第二种就是建两个虚儿子,然后按奇偶接儿子。

回到这道题,我们显然可以用第一种方法建树,这并不影响答案。我们发现我们边分治似乎对点的问题不是很好解决,但是两个点之间的点的个数等于边的个数+\(1\),于是我们就可以通过维护边数得到点数。

然后注意虚边的边权为\(0\)就好了,似乎也没有好讲的(手动划掉)。这里讲一个小细节,将两棵子树答案合并的时候可以满足必须要经过这条边,因为不经过的话可以在子树里面找到。不过实现起来的话,强制经过似乎比较好码。(雾

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define ll long long
#define MAXN 100005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int n,las[MAXN];
ll ans,val[MAXN]; namespace Graph{
#define fi first
#define se second
#define MP make_pair
#define PII pair<ll,ll>
int t,ta,tb,top = 1,to[MAXN << 1],wei[MAXN << 1],nxt[MAXN << 1],head[MAXN];
void Add_Edge (int u,int v,int w){to[++ top] = v,wei[top] = w,nxt[top] = head[u],head[u] = top;}
void AddEdge (int u,int v,int w){Add_Edge (u,v,w),Add_Edge (v,u,w);}
bool vis[MAXN << 1];
int siz[MAXN],lim,ed,Siz;//ed表示当前重心边
void dfs (int u,int fa){
siz[u] = 1;
for (Int i = head[u],v;i;i = nxt[i]){
if (vis[i] || (v = to[i]) == fa) continue;
dfs (v,u),siz[u] += siz[v];
int tmp = max (siz[v],Siz - siz[v]);
if (tmp < lim) lim = tmp,ed = i;
}
}
PII A[MAXN],B[MAXN],*f;
void dfs (int u,int fa,ll minn,ll dis){
minn = min (minn,val[u]),f[++ t] = MP (minn,dis);
for (Int i = head[u],v;i;i = nxt[i]){
if (vis[i] || (v = to[i]) == fa) continue;
dfs (v,u,minn,dis + wei[i]);
}
}
void Solve (int u,int S){//S表示当前子树大小
if (S <= 1) return ;
lim = Siz = S,dfs (u,0),vis[ed] = vis[ed ^ 1] = 1;
t = 0,f = A,dfs (to[ed],0,1e9,0),ta = t;
t = 0,f = B,dfs (to[ed ^ 1],0,1e9,0),tb = t;
sort (A + 1,A + ta + 1),sort (B + 1,B + tb + 1);
int j = tb;ll mx = 0,l = wei[ed];
for (Int i = ta;i;-- i){
while (j > 1 && B[j].fi >= A[i].fi) mx = max (mx,B[j --].se);
if (j < tb) ans = max (ans,(A[i].se + mx + 1 + l) * A[i].fi);
}
j = ta,mx = 0;
for (Int i = tb;i;-- i){
while (j > 1 && A[j].fi >= B[i].fi) mx = max (mx,A[j --].se);
if (j < ta) ans = max (ans,(B[i].se + mx + 1 + l) * B[i].fi);
}
int tx = to[ed],ty = to[ed ^ 1];
Solve (tx,siz[tx]),Solve (ty,S - siz[tx]);
}
} int top = 1,cnt,to[MAXN << 1],nxt[MAXN << 1],head[MAXN];
void Add_Edge (int u,int v){to[++ top] = v,nxt[top] = head[u],head[u] = top;} void dfs (int u,int fa){
for (Int i = head[u],v;i;i = nxt[i]){
if ((v = to[i]) == fa) continue;dfs (v,u);
if (!las[u]) Graph::AddEdge (u,v,1),las[u] = u;
else ++ cnt,Graph::AddEdge (las[u],cnt,0),Graph::AddEdge (las[u] = cnt,v,1),val[cnt] = val[u];
}
} signed main(){
read (n),cnt = n;
for (Int i = 1;i <= n;++ i) read (val[i]);
for (Int i = 2,u,v;i <= n;++ i) read (u,v),Add_Edge (u,v),Add_Edge (v,u);
dfs (1,0),Graph::Solve (1,cnt);
write (ans),putchar ('\n');
return 0;
}

题解 最长道路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

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

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

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

  5. bzoj2870最长道路tree——边分治

    简化版描述: 给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大. 其中链长度定义为链上点的个数.   有几个不同的做法: 1.sort+并查集+树的直径.边从大到小加入 ...

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

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

  7. BZOJ 2870: 最长道路tree 树的直径+并查集

    挺好的一道题. 把所有点都离线下来,一个个往里加入就行了. #include <cstdio> #include <algorithm> #define N 100003 #d ...

  8. 2870: 最长道路tree

    链接 https://www.lydsy.com/JudgeOnline/problem.php?id=2870 思路 先把树转化为二叉树 再链分治 %%yyb 代码 #include <ios ...

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

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

随机推荐

  1. 程序解决十苹果问题 Java

    程序解决十苹果问题 Java 题目:10个苹果,其中有9个重量相同,剩余1个相比其它重量不同(或重或轻,不得而知),使用天平比较三次,找出重量特殊的那一个 import org.junit.Test; ...

  2. 关于Quartus构建nios软核以及eclipse建立c语言工程以及成功下载到FPGA芯片过程遇到的各种问题以及解决方法详解

    这不是一篇构建nios的教程,而是遇到的各种问题以及解决方法.至于构建教程,网上一大把,我推荐正点原子的FPGA教程,比较新,比较详细,通俗易懂!!! 这里以一个点亮LED灯的Nios软核为例,很明显 ...

  3. Python之smtplib模块

    工作中难免会出现自动发送电子邮件的需求,比如说做完自动化测试之后通过电子邮件的形式将结果反馈出来.Python中提供了标准库smtplib来解决这一问题,该模块定义了一个smtp客户端会话对象,能够将 ...

  4. java 线程基础篇,看这一篇就够了。

    前言: Java三大基础框架:集合,线程,io基本是开发必用,面试必问的核心内容,今天我们讲讲线程. 想要把线程理解透彻,这需要具备很多方面的知识和经验,本篇主要是关于线程基础包括线程状态和常用方法. ...

  5. Git 系列教程(13)- 分支管理

    查看分支列表 $ git branch iss53 * master testing  注意 master 分支前的 * 字符:它代表现在 checkout 的那一个分支(也就是说,当前 HEAD 指 ...

  6. C#中的文本到语音

    本演示说明了如何使用c#.net Windows Forms应用程序中的system.speech库将文本转换为语音.Microsoft .NET框架提供System.Speech.Synthesis ...

  7. [第十一篇]——Docker 仓库管理之Spring Cloud直播商城 b2b2c电子商务技术总结

    Docker 仓库管理 仓库(Repository)是集中存放镜像的地方.以下介绍一下 Docker Hub.当然不止 docker hub,只是远程的服务商不一样,操作都是一样的. Docker H ...

  8. docker run配置参数

    Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] -d, --detach=false 指定容器运行于前台还是后台,默认为false -i, - ...

  9. 图论---DFS

    图论---DFS 1. 图的遍历 在理解DFS算法之前,我们首先需要对什么是遍历进行了解,遍历的概念就是:从某一个点出发(一般是首或尾),依次将数据结构中的每一个数据访问且只访问一遍. 2. DFS简 ...

  10. Java空指针异常:java.lang.NullPointerException解决办法

    问题描述:运行maven项目抛出NullPointerException 空指针异常. 报空指针异常的原因有以下几种: 1字符串变量未初始化    例如:String x=null:对象x为null, ...