【BZOJ2870】最长道路(边分治)
【BZOJ2870】最长道路(边分治)
题面
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】最长道路(边分治)的更多相关文章
- BZOJ2870—最长道路tree
最长道路tree Description H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样.每个路口都有很多车辆来往,所以每个路口i都 ...
- bzoj2870最长道路tree——边分治
简化版描述: 给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大. 其中链长度定义为链上点的个数. 有几个不同的做法: 1.sort+并查集+树的直径.边从大到小加入 ...
- [BZOJ2870]最长道路tree:点分治
算法一:点分治+线段树 分析 说是线段树,但是其实要写树状数组卡常. 代码 #include <bits/stdc++.h> #define rin(i,a,b) for(register ...
- BZOJ2870: 最长道路tree
题解: 子树分治的做法可以戳这里:http://blog.csdn.net/iamzky/article/details/41120733 可是码量... 这里介绍另一种好写又快的方法. 我们还是一颗 ...
- BZOJ2870 最长道路
题意:给定树,有点权.求一条路径使得最小点权 * 总点数最大.只需输出这个最大值.5w. 解:树上路径问题,点分治. 考虑合并两个子树的时候,答案的形式是val1 * (d1 + d2),当1是新插入 ...
- BZOJ2870 最长道路tree(并查集+LCA)
题意 (n<=50000) 题解 #include<iostream> #include<cstring> #include<cstdio> #include ...
- 【BZOJ2870】最长道路tree 点分治+树状数组
[BZOJ2870]最长道路tree Description H城很大,有N个路口(从1到N编号),路口之间有N-1边,使得任意两个路口都能互相到达,这些道路的长度我们视作一样.每个路口都有很多车辆来 ...
- bzoj 2870 最长道路tree——边分治
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2870 关于边分治:https://www.cnblogs.com/Khada-Jhin/p/ ...
- 【bzoj2870】最长道路tree 树的直径+并查集
题目描述 给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大. 其中链长度定义为链上点的个数. 输入 第一行N 第二行N个数分别表示1~N的点权v[i] 接下来N-1行每 ...
随机推荐
- Nginx三部曲(1)基础
我们会告诉你 Nginx 是如何工作的,其背后的概念有哪些,以及如何优化它以提升应用程序的性能.还会告诉你如何安装,如何启动.运行. 这个教程包括三节: 基础概念——你可以了解命令(directive ...
- phantomjs 了解
转自:http://www.cnblogs.com/lei0213/ PhantomJS是一个无界面的,可脚本编程的WebKit浏览器引擎.它原生支持多种web 标准:DOM 操作,CSS选择器,JS ...
- opencv2\flann\matrix.h(69): error C2059: 语法错误:“,”
在提示错误的matrix.h头文件中,修改一下,在free前加上_ ,即FLANN_DEPRECATED void _free() .
- syncthing 多主机同步文件工具
周五看了下阮一峰的blog 看到有一个 syncthing的小工具挺好用的 进行了简单的尝试: 1. 下载文件位置: https://syncthing.net 2. 下载文件后的简单安装 绿色版直接 ...
- vue-router路由懒加载(解决vue项目首次加载慢)
懒加载:----------------------------------------------------? 也叫延迟加载,即在需要的时候进行加载,随用随载. 为什么需要懒加载? 像vue这种单 ...
- Python 基础知识----数据类型
一.Number 类型(数值类型) 二.String 类型 (字符串类型) 三.List 类型 (列表类型) 是一种常用的序列类型簇,List 用中括号 [ ] 表示,不同的元素(任意类型的值)之间以 ...
- python3 输出系统信息
一.安装psutil库 pip3 install psutil 二.代码 #!/usr/bin/python3 coding=utf-8 import psutil import uuid impor ...
- fiddler 笔记-设置断点
设置断点后,可以修改httprequest的任何信息包括:host,cookie或都表单中的数据 1 Fiddler--rules--Automatic Breakpoint --before Req ...
- TP5上传图片
模板: <form action="{:url('Temp/addTempDo')}" enctype="multipart/form-data" met ...
- Python——Django-应用的models.py内容
一.数据的相关配置 #数据库的相关配置 DATABASE = { 'default':{ #连接的数据库类型 'ENGINE':'django.db.backends.sqlite3', #连接数据库 ...