

分析 斜率 + 倍增
- 由题可知:
cv−cudis(u,v)=cv−cudepu−depv=−cu−cvdepu−depv
- 把
(depu,cu)
看做一个点,就相当于要求
u
与其祖先的最大斜率
- 显然,最大斜率在u与其祖先组成的下凸壳上(斜率递增)
- 因为要在树上做,暴力求每个点的下凸壳栈可能一次会弹掉很多点,时间复杂度难以承受
- 考虑到斜率递增,就可以用倍增的思想,记
pre[u][k]
表示点
u
在与其祖先组成的下凸壳上往上走2k条边到达的点,则每次通过该数组找到可以相接的点
v
,直接令pre[u][0]=v,再维护出其余
pre[u][k](k>0)
即可
- 时间复杂度显然为
O(nlogn)
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
namespace INOUT
{
const int S = 1 << 20;
char frd[S], *hed = frd + S;
const char *tal = hed;
inline char nxtChar()
{
if (hed == tal)
fread(frd, 1, S, stdin), hed = frd;
return *hed++;
}
inline int get()
{
char ch; int res = 0; bool flag = false;
while (!isdigit(ch = nxtChar()) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (isdigit(ch = nxtChar()))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
};
using namespace INOUT;
const int N = 5e5 + 5;
const double eps = 1e-10;
int c[N], dep[N], g[N][21], n; double rs[N];
struct Edge
{
int to; Edge *nxt;
}p[N], *T = p, *lst[N];
inline void LinkEdge(int x, int y)
{
(++T)->nxt = lst[x]; lst[x] = T; T->to = y;
}
inline double Slo(int x, int y)
{
return (c[x] - c[y]) / (double)(dep[x] - dep[y]);
}
inline void Dfs(int x)
{
for (Edge *e = lst[x]; e; e = e->nxt)
{
int y = e->to, u = x;
dep[y] = dep[x] + 1;
for (int k = 19; k >= 0; --k)
if (rs[g[u][k]] >= Slo(g[u][k], y) - eps)
u = g[u][k];
if (rs[u] >= Slo(u, y) - eps) u = g[u][0];
rs[y] = Slo(y, u);
g[y][0] = u;
for (int k = 0; k < 19; ++k)
g[y][k + 1] = g[g[y][k]][k];
Dfs(y);
}
}
int main()
{
freopen("lost.in", "r", stdin);
freopen("lost.out", "w", stdout);
n = get(); int x;
for (int i = 1; i <= n; ++i) c[i] = get();
for (int i = 2; i <= n; ++i)
x = get(), LinkEdge(x, i);
rs[0] = rs[1] = -1e18;
for (Edge *e = lst[1]; e; e = e->nxt)
{
int y = e->to;
g[y][0] = dep[y] = 1;
rs[y] = Slo(1, y);
Dfs(y);
}
for (int i = 2; i <= n; ++i)
printf("%.10lf\n", -rs[i]);
fclose(stdin); fclose(stdout);
return 0;
}