【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行每 ...
随机推荐
- 《梦断代码》Scott Rosenberg著(三)
开放与封闭之论: 程序源代码是商业软件公司最重要的资产,所以软件公司售卖二进制文件.这样也就意味着如果微软的软件产品出了问题,即便你是一个程序大牛也无法修复它.你只能等着微软来修正问题,因为只有微软程 ...
- h5-canvas(其他api)
###1.使用图片(需要image对象) drawImage(image,x,y,width,height) 其中image是image或者canvas对象,x和y 是其在目标canvas的起始坐标 ...
- [转帖]Linux 硬件和系统配置查看命令小结
https://blog.csdn.net/strongwangjiawei/article/details/8208825 转帖了不少 发现自己记住的还是不多.. Linux 硬件和系统配置查看命令 ...
- Sigma Function
做完这道题,我明白了人生的一个巨大道理,那就是: 其他题研究两下,做出来几百行.数论码字前研究半天,做出来十几二十行.做完特别没有成就感... 首先说下这题题意:首先,定义一个函数f[n],即为他所有 ...
- 校园电商项目2(基于SSM)——模块设计
步骤一:各模块职责 步骤二:实体类设计 package com.figsprite.o2o.bean; import java.util.Date; public class Area { priva ...
- java学习之—递归实现变位字
/** * 递归实现变位字 * Create by Administrator * 2018/6/20 0020 * 上午 10:23 **/ public class AnagramApp { st ...
- jenkins和jdk版本问题
问题:公司业务是用的jdk1.7的,但最新版的jenkins (jenkins-2.138.2-1.1.noarch.rpm)却只支持jdk1.8 分析: 1.公司业务用的jdk1.7不能换,不然影响 ...
- Maven 项目 无缘无故报错:版本冲突,其他机器上正常-提交的时候报冲突怎么也解决不掉
2018年: maven突然之间报错了,显示版本冲突,但是其他的机器是好的, 使用命令:mvn compile -P dev -e; 看看测试环境有没有问题,还是有问题.而且,刚开始只是报错:erro ...
- qtp 自动货测试桌面程序-笔记(使用参数 parameters)
dtGlobalSheet:运行整个test时候使用的参数(心得:可以将公共使用的测试数据放于全局表格中,所有action脚本都可以使用同一个数据,如供应商.客户.商品) dtActionSheet: ...
- SQL 函数NULLIF、NULL、ISNULL、COALESCE、IIF
NULLIF函数 NULLIF(Expression1,Expression2):给定两个参数Expression1和Expression2,如果两个参数相等,则返回NULL:否则就返回第一个参数. ...