题意:给定树,有点权。求一条路径使得最小点权 * 总点数最大。只需输出这个最大值。5w。

解:树上路径问题,点分治。

考虑合并两个子树的时候,答案的形式是val1 * (d1 + d2),当1是新插入的节点的时候,只需在val比它大的点中选出一个最大的d2,这树状数组就可以做到。

当2是新插入的节点时候,好像需要凸包了?但是我们完全不虚啊,因为我们倒序枚举子树就能让2在1之前插入。

于是正反枚举两次子树,拿树状数组维护一下后缀最大值就行了。

复杂度O(nlog2n)

 /**
* There is no end though there is a start in space. ---Infinity.
* It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
* Only the person who was wisdom can read the most foolish one from the history.
* The fish that lives in the sea doesn't know the world in the land.
* It also ruins and goes if they have wisdom.
* It is funnier that man exceeds the speed of light than fish start living in the land.
* It can be said that this is an final ultimatum from the god to the people who can fight.
*
* Steins;Gate
*/ #include <bits/stdc++.h> #define forson(x, i) for(int i = e[x]; i; i = edge[i].nex) typedef long long LL;
const int N = , INF = 0x3f3f3f3f; struct Edge {
int nex, v;
}edge[N << ], edge2[N << ]; int tp, tp2; int e[N], n, siz[N], _n, root, small, e2[N], xx, d[N], Val[N];
LL val[N], X[N], ans;
bool del[N]; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} inline void add2(int x, int y) {
tp2++;
edge2[tp2].v = y;
edge2[tp2].nex = e2[x];
e2[x] = tp2;
return;
} namespace ta {
int ta[N];
inline void add(int x, int v) {
x = xx + - x;
for(int i = x; i <= xx; i += i & (-i)) {
ta[i] = std::max(ta[i], v);
}
return;
}
inline void del(int x) {
x = xx + - x;
for(int i = x; i <= xx; i += i & (-i)) {
ta[i] = -INF;
}
return;
}
inline int getMax(int x) {
x = xx + - x;
int ans = -INF;
for(int i = x; i; i -= i & (-i)) {
ans = std::max(ans, ta[i]);
}
return ans;
}
} void getroot(int x, int f) {
siz[x] = ;
int large = ;
forson(x, i) {
int y = edge[i].v;
if(y == f || del[y]) continue;
getroot(y, x);
siz[x] += siz[y];
if(siz[y] > large) {
large = siz[y];
}
}
if(_n - siz[x] > large) {
large = _n - siz[x];
}
if(small > large) {
small = large;
root = x;
}
return;
} void DFS_1(int x, int f) {
siz[x] = ;
d[x] = d[f] + ;
Val[x] = std::min(Val[f], (int)val[x]);
ans = std::max(ans, X[Val[x]] * (d[x] + ta::getMax(Val[x])));
forson(x, i) {
int y = edge[i].v;
if(del[y] || y == f) continue;
DFS_1(y, x);
siz[x] += siz[y];
}
return;
} void DFS_2(int x, int f) {
ta::add(Val[x], d[x] + );
forson(x, i) {
int y = edge[i].v;
if(y == f || del[y]) continue;
DFS_2(y, x);
}
return;
} void DFS_3(int x, int f) {
ta::del(Val[x]);
forson(x, i) {
int y = edge[i].v;
if(del[y] || y == f) {
continue;
}
DFS_3(y, x);
}
return;
} void poi_div(int x) {
small = INF;
getroot(x, );
x = root; d[x] = ;
Val[x] = val[x];
ta::add(Val[x], );
forson(x, i) {
int y = edge[i].v;
if(del[y]) continue;
DFS_1(y, x);
DFS_2(y, x);
}
DFS_3(x, );
// ---------
for(int i = e2[x]; i; i = edge2[i].nex) {
int y = edge2[i].v;
if(del[y]) continue;
DFS_1(y, x);
DFS_2(y, x);
}
ans = std::max(ans, X[val[x]] * ta::getMax(val[x]));
DFS_3(x, ); del[x] = ;
forson(x, i) {
int y = edge[i].v;
if(del[y]) continue;
_n = siz[y];
poi_div(y);
}
return;
} int main() { scanf("%d", &n);
for(int i = ; i <= n; i++) {
scanf("%lld", &val[i]);
X[i] = val[i];
}
std::sort(X + , X + n + );
xx = std::unique(X + , X + n + ) - X - ;
for(int i = ; i <= n; i++) {
val[i] = std::lower_bound(X + , X + xx + , val[i]) - X;
}
ans = X[xx];
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y); add(y, x);
}
for(int x = ; x <= n; x++) {
forson(x, i) {
add2(x, edge[i].v);
}
} poi_div(); printf("%lld\n", ans);
return ;
}

AC代码

题外话:感觉能树形DP,但是要线段树合并 + 凸包合并,虚的一批...

还发现了一个O(nlogn)的做法,只需多叉转二叉然后O(n) - O(1)lca即可实现,瓶颈在于排序...

然而这题是边分治模板题...

BZOJ2870 最长道路的更多相关文章

  1. BZOJ2870—最长道路tree

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

  2. BZOJ2870: 最长道路tree

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 【BZOJ2870】最长道路

    权限题 题意 给出一棵树,点有点权,找到树上的一条路径使得路径上点的个数和其中点权最小的点的点权之积最大,输出最大值. Sol 边分治板子题啦. 边分治后对于分出来的两棵子树 , 按到左右根的最小点权 ...

随机推荐

  1. Python编写脚本(输出三星形状的‘*’符号)

    环境:python3.* 心得:个人认为脚本非我强项,以下效果可以有更简单解决方案,纯属练习逻辑. 方案一: s=1 while s<=10: #这是决定多少列,起始为1,大循环一圈即加一,就是 ...

  2. c/c++ 多线程 等待一次性事件 异常处理

    多线程 等待一次性事件 异常处理 背景:假设某个future在等待另一个线程结束,但是在被future等待的线程里发生了异常(throw一个异常A),这时怎么处理. 结果:假设发生了上面的场景,则在调 ...

  3. 微信js-sdk开发获取签名和获取地理位置接口示例

    ###微信js-sdk开发获取签名和获取地理位置接口示例 前言:在做微信公众号开发时需要获取用户的地理位置信息,之前通过高德或者百度.腾讯等地图的api时发现经常获取不到,毕竟第三方的东西,后来改为采 ...

  4. GO语言学习笔记(一)

    GO语言学习笔记 1.数组切片slice:可动态增长的数组 2.错误处理流程关键字:defer panic recover 3.变量的初始化:以下效果一样 `var a int = 10` `var ...

  5. 浏览器和服务器实现跨域(CORS)判定的原理

    前端对Cross-Origin Resource Sharing 问题(CORS,中文又称'跨域')应该很熟悉了.众所周知出于安全的考虑,浏览器有个同源策略,对于不同源的站点之间的相互请求会做限制(跨 ...

  6. vue中的watch方法 实时同步存储数据

    watch 监视模式里面有个独特的方法handler 注意要加上deep: true.deep为true时,当对象的key值改变时也监听 当值发生改变被watch监视到触发了事件 开始执行handle ...

  7. yum makecache

    $ yum makecache 就是把服务器的包信息下载到本地电脑缓存起来,makecache建立一个缓存,以后用install时就在缓存中搜索,提高了速度.配合yum -C search xxx使用 ...

  8. cookie跨域共享

    domain和path属性,domain就是当前域,默认为请求的地址,如网址为www.jb51.net/test/test.aspx,那么domain默认为www.jb51.net,path默认就是当 ...

  9. Java基础——枚举详解

    前言: 在第一次学习面向对象编程时,我记得最深的一句话就是“万物皆对象”.于是我一直秉承着这个思想努力的学习着JAVA,直到学习到枚举(Enum)时,看着它颇为奇怪的语法……我一直在想,这TM是个什么 ...

  10. 更改电脑名称后, Cnario无法播放画面和声音, 开机后停留在桌面, Cnario Player软件界面的停止按钮为蓝色可选状态

    症状描述 Cnario Player正常工作期间, 更改了电脑的Windows系统计算机名称(不是登录Windows的用户名), 重启后, 新计算机名生效. 此时Cnario自动启动, 但没有进入播放 ...