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

题面

BZOJ权限题

Description

H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样。每个路口都有很多车辆来往,所以每个路口i都有一个拥挤程度v[i],我们认为从路口s走到路口t的痛苦程度为s到t的路径上拥挤程度的最小值,乘上这条路径上的路口个数所得的积。现在请你求出痛苦程度最大的一条路径,你只需输出这个痛苦程度。

简化版描述:

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

其中链长度定义为链上点的个数。

Input

第一行N

第二行N个数分别表示1~N的点权v[i]

接下来N-1行每行两个数x、y,表示一条连接x和y的边

Output

一个数,表示最大的痛苦程度。

Sample Input

3

5 3 5

1 2

1 3

Sample Output

10

【样例解释】

选择从1到3的路径,痛苦程度为min(5,5)*2=10

题解

看到这个题就先想到了点分治。

然而对于重心的子树而言合并两条链是一件很蛋疼的事情。(不过确实是可以做的)

所以换种方法来考虑。

点分治是考虑过一个点的所有路径的答案。

这次考虑边,考虑经过这条边的所有路径的答案。

类似点分治,我们需要每次找出一条边来,使得其左右两侧的子树大小最接近,然后处理过这条边的答案然后把这条边删掉左右递归处理。

不难发现在菊花图上随便卡烂。

那么来优化,如果一个点存在多个儿子,就类似线段树一样给他建立若干虚点形成一个二叉树,这样子点数最多翻倍,而菊花图就卡不烂了。

考虑如何合并左右两条边,把所有链给取下来之后按照链上最小权值从大往小排序。

这样子直接枚举其中一条边,强制在另外一侧中选一个权值比他大的链,那么就可以求出其中的最大长度组合。

注意一下虚点构出来之后,其权值等于其父亲的权值,虚点连边的边权设为\(0\),而树边边权设为\(1\),这样子就可以维护链的长度了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define MAX 200200
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,N,v[MAX];ll ans;
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
vector<int> son[MAX];
void dfs(int u,int ff)
{
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
son[u].push_back(e[i].v),dfs(e[i].v,u);
}
void ReBuild()
{
cnt=2;memset(h,0,sizeof(h));
for(int i=1;i<=n;++i)
{
int l=son[i].size();
if(l<=2)
for(int j=0;j<l;++j)
{
Add(i,son[i][j],son[i][j]<=N);
Add(son[i][j],i,son[i][j]<=N);
}
else
{
int s1=++n,s2=++n;v[s1]=v[s2]=v[i];
Add(i,s1,0);Add(s1,i,0);Add(i,s2,0);Add(s2,i,0);
for(int j=0;j<l;++j)
if(j&1)son[s1].push_back(son[i][j]);
else son[s2].push_back(son[i][j]);
}
}
}
int size[MAX];
bool vis[MAX];
int rt,mx,Size;
void getroot(int u,int ff,int Size)
{
size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(vis[i>>1]||v==ff)continue;
getroot(v,u,Size);size[u]+=size[v];
int ret=max(size[v],Size-size[v]);
if(mx>ret)mx=ret,rt=i;
}
}
struct Pair{int l,v;}S[2][MAX];
bool operator<(Pair a,Pair b){return a.v>b.v;}
int top[2];
void dfs(int u,int ff,int len,int mn,int opt)
{
mn=min(mn,v[u]);S[opt][++top[opt]]=(Pair){len,mn};
for(int i=h[u];i;i=e[i].next)
if(!vis[i>>1]&&e[i].v!=ff)
dfs(e[i].v,u,len+e[i].w,mn,opt);
}
void Divide(int u,int Size)
{
mx=1e9;getroot(u,0,Size);
if(mx>=1e9)return;vis[rt>>1]=true;
top[0]=top[1]=0;
dfs(e[rt].v,0,0,1e9,0);
dfs(e[rt^1].v,0,0,1e9,1);
sort(&S[0][1],&S[0][top[0]+1]);
sort(&S[1][1],&S[1][top[1]+1]);
for(int i=1,j=1,mx=0;i<=top[0];++i)
{
while(j<=top[1]&&S[1][j].v>=S[0][i].v)mx=max(mx,S[1][j++].l);
if(j>1)ans=max(ans,1ll*(mx+S[0][i].l+1+e[rt].w)*S[0][i].v);
}
for(int i=1,j=1,mx=0;i<=top[1];++i)
{
while(j<=top[0]&&S[0][j].v>=S[1][i].v)mx=max(mx,S[0][j++].l);
if(j>1)ans=max(ans,1ll*(mx+S[1][i].l+1+e[rt].w)*S[1][i].v);
}
int nw=rt,S2=Size-size[e[rt].v];
Divide(e[nw].v,size[e[rt].v]);
Divide(e[nw^1].v,S2);
}
int main()
{
n=read();N=n;
for(int i=1;i<=n;++i)v[i]=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v,1);Add(v,u,1);
}
dfs(1,0);ReBuild();
Divide(1,n);
printf("%lld\n",ans);
return 0;
}

【BZOJ2870】最长道路(边分治)的更多相关文章

  1. BZOJ2870—最长道路tree

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

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

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

  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. BZOJ2870 最长道路

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

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

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

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

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

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

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

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

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

随机推荐

  1. iOS开发——无网占位图的实现

    https://www.jianshu.com/p/d537393fe247 https://github.com/wyzxc/CQPlaceholderViewhttps://github.com/ ...

  2. Centos 6.x 升级到 7.x

    Centos6.5跨越大版本升级到Centos7.4 - Linux学习与应用 - CSDN博客https://blog.csdn.net/whbttst/article/details/805348 ...

  3. react render

    实际上react render方法返回一个虚拟dom 并没有去执行渲染dom 渲染的过程是交给react 去完成的 这就说明了为什么要在所有数据请求完成后才去实现render 这样做也提高了性能.只调 ...

  4. 简单易懂的softmax交叉熵损失函数求导

    参考: https://blog.csdn.net/qian99/article/details/78046329

  5. Zookeeper的作用,在Hadoop及hbase中具体作用

    什么是Zookeeper,Zookeeper的作用是什么,在Hadoop及hbase中具体作用是什么 一.什么是Zookeeper ZooKeeper 顾名思义 动物园管理员,他是拿来管大象(Hado ...

  6. Git使用:Linux(Ubuntu 14.04 x64)下安装Git并配置连接GitHub

    github是一个非常好的网络代码托管仓库,知晓许久,但是一直没有用起来,最近才开始使用git管理自己的文档和代码. Git是非常强大的版本管理工具,今天就告诉大家,如何在Linux下安装GIt,并且 ...

  7. python之路--初识函数

    一 . 函数 什么是函数 f(x) = x + 1 y = x + 1 # 函数是对功能或者动作的封装 函数的语法 def 函数名(): 函数体 调用: 函数名() def play(): print ...

  8. python学习笔记(8)--random库的使用

    伪随机数:采用梅森旋转算法生成的伪随机序列中元素 使用random库 一.基本随机函数 随机数需要一个种子,依据这个种子通过梅森旋转算法产生固定序列的随机数.seed(a=None)  初始化给定的随 ...

  9. python数学第七天【期望的性质】

  10. python web需要了解哪些

    1. socket.tcp/ip.http(cookie.session.token).https.ssl 2. wsgi:https://www.python.org/dev/peps/pep-33 ...